c56a3ce0e0d489825f8afe225d0b50ed9fbd7444
[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 = 30000;
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   DCHECK(waiting_to_handle_position_);
339   DCHECK(drag_drop_client_);
340   DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
341
342   scoped_refptr<base::RefCountedMemory> data;
343   ::Atom type = None;
344   if (ui::GetRawBytesOfProperty(local_window_, event.property,
345                                 &data, NULL, NULL, &type)) {
346     fetched_targets_.Insert(event.target, data);
347   }
348
349   if (!unfetched_targets_.empty()) {
350     RequestNextTarget();
351   } else {
352     waiting_to_handle_position_ = false;
353     drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
354     drag_drop_client_ = NULL;
355   }
356 }
357
358 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
359   DesktopDragDropClientAuraX11* client =
360       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
361   if (!client) {
362     std::vector<Atom> atom_array;
363     if (!ui::GetAtomArrayProperty(source_window_,
364                                   "XdndActionList",
365                                   &atom_array)) {
366       actions_.clear();
367     } else {
368       actions_.swap(atom_array);
369     }
370   } else {
371     // We have a property notify set up for other windows in case they change
372     // their action list. Thankfully, the views interface is static and you
373     // can't change the action list after you enter StartDragAndDrop().
374     actions_ = client->GetOfferedDragOperations();
375   }
376 }
377
378 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
379   int drag_operation = ui::DragDropTypes::DRAG_NONE;
380   for (std::vector<Atom>::const_iterator it = actions_.begin();
381        it != actions_.end(); ++it) {
382     MaskOpeartion(*it, &drag_operation);
383   }
384
385   MaskOpeartion(suggested_action_, &drag_operation);
386
387   return drag_operation;
388 }
389
390 void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion(
391     ::Atom xdnd_operation,
392     int* drag_operation) const {
393   if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
394     *drag_operation |= ui::DragDropTypes::DRAG_COPY;
395   else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
396     *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
397   else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
398     *drag_operation |= ui::DragDropTypes::DRAG_LINK;
399 }
400
401 uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
402     const base::NativeEvent& event) {
403   if (event->type == PropertyNotify &&
404       event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
405     ReadActions();
406   }
407   return POST_DISPATCH_NONE;
408 }
409
410 ///////////////////////////////////////////////////////////////////////////////
411
412 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
413     aura::Window* root_window,
414     views::DesktopNativeCursorManager* cursor_manager,
415     Display* xdisplay,
416     ::Window xwindow)
417     : move_loop_(this),
418       root_window_(root_window),
419       xdisplay_(xdisplay),
420       xwindow_(xwindow),
421       atom_cache_(xdisplay_, kAtomsToCache),
422       target_window_(NULL),
423       source_provider_(NULL),
424       source_current_window_(None),
425       source_state_(SOURCE_STATE_OTHER),
426       drag_operation_(0),
427       resulting_operation_(0),
428       grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
429       copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
430       move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
431       weak_ptr_factory_(this) {
432   DCHECK(g_live_client_map.Get().find(xwindow) ==
433          g_live_client_map.Get().end());
434   g_live_client_map.Get().insert(std::make_pair(xwindow, this));
435
436   // Mark that we are aware of drag and drop concepts.
437   unsigned long xdnd_version = kMinXdndVersion;
438   XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
439                   XA_ATOM, 32, PropModeReplace,
440                   reinterpret_cast<unsigned char*>(&xdnd_version), 1);
441 }
442
443 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
444   g_live_client_map.Get().erase(xwindow_);
445   // Make sure that all observers are unregistered from source and target
446   // windows. This may be necessary when the parent native widget gets destroyed
447   // while a drag operation is in progress.
448   NotifyDragLeave();
449 }
450
451 // static
452 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
453     ::Window window) {
454   std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
455       g_live_client_map.Get().find(window);
456   if (it == g_live_client_map.Get().end())
457     return NULL;
458   return it->second;
459 }
460
461 void DesktopDragDropClientAuraX11::OnXdndEnter(
462     const XClientMessageEvent& event) {
463   DVLOG(1) << "XdndEnter";
464
465   int version = (event.data.l[1] & 0xff000000) >> 24;
466   if (version < 3) {
467     LOG(ERROR) << "Received old XdndEnter message.";
468     return;
469   }
470
471   // Make sure that we've run ~X11DragContext() before creating another one.
472   target_current_context_.reset();
473   target_current_context_.reset(
474       new X11DragContext(&atom_cache_, xwindow_, event));
475
476   // In the Windows implementation, we immediately call DesktopDropTargetWin::
477   // Translate(). Here, we wait until we receive an XdndPosition message
478   // because the enter message doesn't convey any positioning
479   // information.
480 }
481
482 void DesktopDragDropClientAuraX11::OnXdndLeave(
483     const XClientMessageEvent& event) {
484   DVLOG(1) << "XdndLeave";
485   NotifyDragLeave();
486   target_current_context_.reset();
487 }
488
489 void DesktopDragDropClientAuraX11::OnXdndPosition(
490     const XClientMessageEvent& event) {
491   DVLOG(1) << "XdndPosition";
492
493   unsigned long source_window = event.data.l[0];
494   int x_root_window = event.data.l[2] >> 16;
495   int y_root_window = event.data.l[2] & 0xffff;
496   ::Atom suggested_action = event.data.l[4];
497
498   if (!target_current_context_.get()) {
499     NOTREACHED();
500     return;
501   }
502
503   // If we already have all the data from this drag, we complete it
504   // immediately.
505   target_current_context_->OnStartXdndPositionMessage(
506       this, suggested_action, source_window,
507       gfx::Point(x_root_window, y_root_window));
508 }
509
510 void DesktopDragDropClientAuraX11::OnXdndStatus(
511     const XClientMessageEvent& event) {
512   DVLOG(1) << "XdndStatus";
513
514   unsigned long source_window = event.data.l[0];
515
516   waiting_on_status_.erase(source_window);
517   if (source_window != source_current_window_)
518     return;
519
520   int drag_operation = ui::DragDropTypes::DRAG_NONE;
521   if (event.data.l[1] & 1) {
522     ::Atom atom_operation = event.data.l[4];
523     negotiated_operation_[source_window] = atom_operation;
524     drag_operation = AtomToDragOperation(atom_operation);
525   }
526
527   switch (drag_operation) {
528     case ui::DragDropTypes::DRAG_COPY:
529       move_loop_.UpdateCursor(copy_grab_cursor_);
530       break;
531     case ui::DragDropTypes::DRAG_MOVE:
532       move_loop_.UpdateCursor(move_grab_cursor_);
533       break;
534     default:
535       move_loop_.UpdateCursor(grab_cursor_);
536       break;
537   }
538
539   // Note: event.data.[2,3] specify a rectangle. It is a request by the other
540   // window to not send further XdndPosition messages while the cursor is
541   // within it. However, it is considered advisory and (at least according to
542   // the spec) the other side must handle further position messages within
543   // it. GTK+ doesn't bother with this, so neither should we.
544
545   if (source_state_ == SOURCE_STATE_PENDING_DROP) {
546     // We were waiting on the status message so we could send the XdndDrop.
547     source_state_ = SOURCE_STATE_DROPPED;
548     SendXdndDrop(source_window);
549     return;
550   }
551
552   NextPositionMap::iterator it = next_position_message_.find(source_window);
553   if (source_state_ == SOURCE_STATE_OTHER &&
554       it != next_position_message_.end()) {
555     // We were waiting on the status message so we could send off the next
556     // position message we queued up.
557     gfx::Point p = it->second.first;
558     unsigned long time = it->second.second;
559     next_position_message_.erase(it);
560
561     SendXdndPosition(source_window, p, time);
562   }
563 }
564
565 void DesktopDragDropClientAuraX11::OnXdndFinished(
566     const XClientMessageEvent& event) {
567   DVLOG(1) << "XdndFinished";
568   resulting_operation_ = AtomToDragOperation(
569       negotiated_operation_[event.data.l[0]]);
570   move_loop_.EndMoveLoop();
571 }
572
573 void DesktopDragDropClientAuraX11::OnXdndDrop(
574     const XClientMessageEvent& event) {
575   DVLOG(1) << "XdndDrop";
576
577   unsigned long source_window = event.data.l[0];
578
579   int drag_operation = ui::DragDropTypes::DRAG_NONE;
580   if (target_window_) {
581     aura::client::DragDropDelegate* delegate =
582         aura::client::GetDragDropDelegate(target_window_);
583     if (delegate) {
584       ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
585           xwindow_, target_current_context_->fetched_targets()));
586
587       ui::DropTargetEvent event(data,
588                                 target_window_location_,
589                                 target_window_root_location_,
590                                 target_current_context_->GetDragOperation());
591       drag_operation = delegate->OnPerformDrop(event);
592     }
593
594     target_window_->RemoveObserver(this);
595     target_window_ = NULL;
596   }
597
598   XEvent xev;
599   xev.xclient.type = ClientMessage;
600   xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
601   xev.xclient.format = 32;
602   xev.xclient.window = source_window;
603   xev.xclient.data.l[0] = xwindow_;
604   xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
605   xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
606
607   SendXClientEvent(source_window, &xev);
608 }
609
610 void DesktopDragDropClientAuraX11::OnSelectionNotify(
611     const XSelectionEvent& xselection) {
612   if (!target_current_context_) {
613     NOTIMPLEMENTED();
614     return;
615   }
616
617   target_current_context_->OnSelectionNotify(xselection);
618 }
619
620 int DesktopDragDropClientAuraX11::StartDragAndDrop(
621     const ui::OSExchangeData& data,
622     aura::Window* root_window,
623     aura::Window* source_window,
624     const gfx::Point& root_location,
625     int operation,
626     ui::DragDropTypes::DragEventSource source) {
627   source_current_window_ = None;
628   DCHECK(!g_current_drag_drop_client);
629   g_current_drag_drop_client = this;
630   waiting_on_status_.clear();
631   source_state_ = SOURCE_STATE_OTHER;
632   drag_operation_ = operation;
633   resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
634
635   const ui::OSExchangeData::Provider* provider = &data.provider();
636   source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
637       provider);
638
639   source_provider_->TakeOwnershipOfSelection();
640
641   std::vector< ::Atom> actions = GetOfferedDragOperations();
642   if (!source_provider_->file_contents_name().empty()) {
643     actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
644     ui::SetStringProperty(
645         xwindow_,
646         atom_cache_.GetAtom(kXdndDirectSave0),
647         atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
648         source_provider_->file_contents_name().AsUTF8Unsafe());
649   }
650   ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
651
652   // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
653   // move loop, which would also destroy this drag-client. So keep track of
654   // whether it is alive after the drag ends.
655   base::WeakPtr<DesktopDragDropClientAuraX11> alive(
656       weak_ptr_factory_.GetWeakPtr());
657
658   // Windows has a specific method, DoDragDrop(), which performs the entire
659   // drag. We have to emulate this, so we spin off a nested runloop which will
660   // track all cursor movement and reroute events to a specific handler.
661   move_loop_.SetDragImage(source_provider_->GetDragImage(),
662                           source_provider_->GetDragImageOffset());
663   move_loop_.RunMoveLoop(source_window, grab_cursor_);
664
665   if (alive) {
666     move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
667
668     source_provider_ = NULL;
669     g_current_drag_drop_client = NULL;
670     drag_operation_ = 0;
671     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
672     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
673
674     return resulting_operation_;
675   }
676   return ui::DragDropTypes::DRAG_NONE;
677 }
678
679 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
680                                               const ui::LocatedEvent& event) {
681   NOTIMPLEMENTED();
682 }
683
684 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
685                                         const ui::LocatedEvent& event) {
686   NOTIMPLEMENTED();
687 }
688
689 void DesktopDragDropClientAuraX11::DragCancel() {
690   move_loop_.EndMoveLoop();
691 }
692
693 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
694   return !!g_current_drag_drop_client;
695 }
696
697 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
698   DCHECK_EQ(target_window_, window);
699   target_window_ = NULL;
700 }
701
702 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
703   gfx::Point screen_point(event->x_root, event->y_root);
704
705   if (source_state_ != SOURCE_STATE_OTHER)
706     return;
707
708   // Find the current window the cursor is over.
709   ::Window mouse_window = None;
710   ::Window dest_window = None;
711   FindWindowFor(screen_point, &mouse_window, &dest_window,
712                 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
713
714   if (source_current_window_ != dest_window) {
715     if (source_current_window_ != None)
716       SendXdndLeave(source_current_window_);
717
718     source_current_window_ = dest_window;
719
720     if (source_current_window_ != None) {
721       negotiated_operation_.erase(source_current_window_);
722       SendXdndEnter(source_current_window_);
723     }
724   }
725
726   if (source_current_window_ != None) {
727     if (ContainsKey(waiting_on_status_, dest_window)) {
728       next_position_message_[dest_window] =
729           std::make_pair(screen_point, event->time);
730     } else {
731       SendXdndPosition(dest_window, screen_point, event->time);
732     }
733   }
734 }
735
736 void DesktopDragDropClientAuraX11::OnMouseReleased() {
737   if (source_current_window_ != None) {
738     if (ContainsKey(waiting_on_status_, source_current_window_)) {
739       // If we are waiting for an XdndStatus message, we need to wait for it to
740       // complete.
741       source_state_ = SOURCE_STATE_PENDING_DROP;
742
743       // Start timer to end the move loop if the target takes too long to send
744       // the XdndStatus and XdndFinished messages.
745       StartEndMoveLoopTimer();
746       return;
747     }
748
749     std::map< ::Window, ::Atom>::iterator it =
750         negotiated_operation_.find(source_current_window_);
751     if (it != negotiated_operation_.end() && it->second != None) {
752       // We have negotiated an action with the other end.
753       source_state_ = SOURCE_STATE_DROPPED;
754       SendXdndDrop(source_current_window_);
755
756       // Start timer to end the move loop if the target takes too long to send
757       // an XdndFinished message.
758       StartEndMoveLoopTimer();
759       return;
760     }
761
762     SendXdndLeave(source_current_window_);
763     source_current_window_ = None;
764   }
765
766   move_loop_.EndMoveLoop();
767 }
768
769 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
770   if (source_current_window_ != None)
771     SendXdndLeave(source_current_window_);
772   target_current_context_.reset();
773   source_state_ = SOURCE_STATE_OTHER;
774   end_move_loop_timer_.Stop();
775 }
776
777 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
778   end_move_loop_timer_.Start(FROM_HERE,
779                              base::TimeDelta::FromMilliseconds(
780                                  kEndMoveLoopTimeoutMs),
781                              this,
782                              &DesktopDragDropClientAuraX11::EndMoveLoop);
783 }
784
785 void DesktopDragDropClientAuraX11::EndMoveLoop() {
786   move_loop_.EndMoveLoop();
787 }
788
789 void DesktopDragDropClientAuraX11::DragTranslate(
790     const gfx::Point& root_window_location,
791     scoped_ptr<ui::OSExchangeData>* data,
792     scoped_ptr<ui::DropTargetEvent>* event,
793     aura::client::DragDropDelegate** delegate) {
794   gfx::Point root_location = root_window_location;
795   root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
796   aura::Window* target_window =
797       root_window_->GetEventHandlerForPoint(root_location);
798   bool target_window_changed = false;
799   if (target_window != target_window_) {
800     if (target_window_)
801       NotifyDragLeave();
802     target_window_ = target_window;
803     if (target_window_)
804       target_window_->AddObserver(this);
805     target_window_changed = true;
806   }
807   *delegate = NULL;
808   if (!target_window_)
809     return;
810   *delegate = aura::client::GetDragDropDelegate(target_window_);
811   if (!*delegate)
812     return;
813
814   data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
815       xwindow_, target_current_context_->fetched_targets())));
816   gfx::Point location = root_location;
817   aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
818
819   target_window_location_ = location;
820   target_window_root_location_ = root_location;
821
822   event->reset(new ui::DropTargetEvent(
823       *(data->get()),
824       location,
825       root_location,
826       target_current_context_->GetDragOperation()));
827   if (target_window_changed)
828     (*delegate)->OnDragEntered(*event->get());
829 }
830
831 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
832   if (!target_window_)
833     return;
834   DragDropDelegate* delegate =
835       aura::client::GetDragDropDelegate(target_window_);
836   if (delegate)
837     delegate->OnDragExited();
838   target_window_->RemoveObserver(this);
839   target_window_ = NULL;
840 }
841
842 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
843     int drag_operation) {
844   if (drag_operation & ui::DragDropTypes::DRAG_COPY)
845     return atom_cache_.GetAtom(kXdndActionCopy);
846   if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
847     return atom_cache_.GetAtom(kXdndActionMove);
848   if (drag_operation & ui::DragDropTypes::DRAG_LINK)
849     return atom_cache_.GetAtom(kXdndActionLink);
850
851   return None;
852 }
853
854 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
855   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
856     return ui::DragDropTypes::DRAG_COPY;
857   if (atom == atom_cache_.GetAtom(kXdndActionMove))
858     return ui::DragDropTypes::DRAG_MOVE;
859   if (atom == atom_cache_.GetAtom(kXdndActionLink))
860     return ui::DragDropTypes::DRAG_LINK;
861
862   return ui::DragDropTypes::DRAG_NONE;
863 }
864
865 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
866   std::vector< ::Atom> operations;
867   if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
868     operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
869   if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
870     operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
871   if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
872     operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
873   return operations;
874 }
875
876 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
877   return source_provider_ ? source_provider_->GetFormatMap() :
878       ui::SelectionFormatMap();
879 }
880
881 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
882     ::Window source_window,
883     const gfx::Point& screen_point) {
884   int drag_operation = ui::DragDropTypes::DRAG_NONE;
885   scoped_ptr<ui::OSExchangeData> data;
886   scoped_ptr<ui::DropTargetEvent> drop_target_event;
887   DragDropDelegate* delegate = NULL;
888   DragTranslate(screen_point, &data, &drop_target_event, &delegate);
889   if (delegate)
890     drag_operation = delegate->OnDragUpdated(*drop_target_event);
891
892   // Sends an XdndStatus message back to the source_window. l[2,3]
893   // theoretically represent an area in the window where the current action is
894   // the same as what we're returning, but I can't find any implementation that
895   // actually making use of this. A client can return (0, 0) and/or set the
896   // first bit of l[1] to disable the feature, and it appears that gtk neither
897   // sets this nor respects it if set.
898   XEvent xev;
899   xev.xclient.type = ClientMessage;
900   xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
901   xev.xclient.format = 32;
902   xev.xclient.window = source_window;
903   xev.xclient.data.l[0] = xwindow_;
904   xev.xclient.data.l[1] = (drag_operation != 0) ?
905       (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
906   xev.xclient.data.l[2] = 0;
907   xev.xclient.data.l[3] = 0;
908   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
909
910   SendXClientEvent(source_window, &xev);
911 }
912
913 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
914   XEvent xev;
915   xev.xclient.type = ClientMessage;
916   xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
917   xev.xclient.format = 32;
918   xev.xclient.window = dest_window;
919   xev.xclient.data.l[0] = xwindow_;
920   xev.xclient.data.l[1] = (kMinXdndVersion << 24);  // The version number.
921   xev.xclient.data.l[2] = 0;
922   xev.xclient.data.l[3] = 0;
923   xev.xclient.data.l[4] = 0;
924
925   std::vector<Atom> targets;
926   source_provider_->RetrieveTargets(&targets);
927
928   if (targets.size() > 3) {
929     xev.xclient.data.l[1] |= 1;
930     ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
931   } else {
932     // Pack the targets into the enter message.
933     for (size_t i = 0; i < targets.size(); ++i)
934       xev.xclient.data.l[2 + i] = targets[i];
935   }
936
937   SendXClientEvent(dest_window, &xev);
938 }
939
940 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
941   // If we're sending a leave message, don't wait for status messages anymore.
942   waiting_on_status_.erase(dest_window);
943   NextPositionMap::iterator it = next_position_message_.find(dest_window);
944   if (it != next_position_message_.end())
945     next_position_message_.erase(it);
946
947   XEvent xev;
948   xev.xclient.type = ClientMessage;
949   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
950   xev.xclient.format = 32;
951   xev.xclient.window = dest_window;
952   xev.xclient.data.l[0] = xwindow_;
953   xev.xclient.data.l[1] = 0;
954   xev.xclient.data.l[2] = 0;
955   xev.xclient.data.l[3] = 0;
956   xev.xclient.data.l[4] = 0;
957   SendXClientEvent(dest_window, &xev);
958 }
959
960 void DesktopDragDropClientAuraX11::SendXdndPosition(
961     ::Window dest_window,
962     const gfx::Point& screen_point,
963     unsigned long time) {
964   waiting_on_status_.insert(dest_window);
965
966   XEvent xev;
967   xev.xclient.type = ClientMessage;
968   xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
969   xev.xclient.format = 32;
970   xev.xclient.window = dest_window;
971   xev.xclient.data.l[0] = xwindow_;
972   xev.xclient.data.l[1] = 0;
973   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
974   xev.xclient.data.l[3] = time;
975   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
976   SendXClientEvent(dest_window, &xev);
977 }
978
979 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
980   XEvent xev;
981   xev.xclient.type = ClientMessage;
982   xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
983   xev.xclient.format = 32;
984   xev.xclient.window = dest_window;
985   xev.xclient.data.l[0] = xwindow_;
986   xev.xclient.data.l[1] = 0;
987   xev.xclient.data.l[2] = CurrentTime;
988   xev.xclient.data.l[3] = None;
989   xev.xclient.data.l[4] = None;
990   SendXClientEvent(dest_window, &xev);
991 }
992
993 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
994                                                     XEvent* xev) {
995   DCHECK_EQ(ClientMessage, xev->type);
996
997   // Don't send messages to the X11 message queue if we can help it.
998   DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
999   if (short_circuit) {
1000     Atom message_type = xev->xclient.message_type;
1001     if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1002       short_circuit->OnXdndEnter(xev->xclient);
1003       return;
1004     } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1005       short_circuit->OnXdndLeave(xev->xclient);
1006       return;
1007     } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1008       short_circuit->OnXdndPosition(xev->xclient);
1009       return;
1010     } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1011       short_circuit->OnXdndStatus(xev->xclient);
1012       return;
1013     } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1014       short_circuit->OnXdndFinished(xev->xclient);
1015       return;
1016     } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1017       short_circuit->OnXdndDrop(xev->xclient);
1018       return;
1019     }
1020   }
1021
1022   // I don't understand why the GTK+ code is doing what it's doing here. It
1023   // goes out of its way to send the XEvent so that it receives a callback on
1024   // success or failure, and when it fails, it then sends an internal
1025   // GdkEvent about the failed drag. (And sending this message doesn't appear
1026   // to go through normal xlib machinery, but instead passes through the low
1027   // level xProto (the x11 wire format) that I don't understand.
1028   //
1029   // I'm unsure if I have to jump through those hoops, or if XSendEvent is
1030   // sufficient.
1031   XSendEvent(xdisplay_, xid, False, 0, xev);
1032 }
1033
1034 }  // namespace views