Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
6
7 #include <X11/Xatom.h>
8
9 #include "base/event_types.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "ui/aura/client/capture_client.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/window_tree_host.h"
16 #include "ui/base/clipboard/clipboard.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_foreign_window_manager.h"
22 #include "ui/base/x/x11_util.h"
23 #include "ui/events/event.h"
24 #include "ui/events/platform/platform_event_source.h"
25 #include "ui/gfx/image/image_skia.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
29 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
30 #include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h"
31 #include "ui/views/widget/widget.h"
32 #include "ui/wm/public/drag_drop_client.h"
33 #include "ui/wm/public/drag_drop_delegate.h"
34
35 using aura::client::DragDropDelegate;
36 using ui::OSExchangeData;
37
38 namespace {
39
40 const int kMinXdndVersion = 5;
41
42 const int kWillAcceptDrop = 1;
43 const int kWantFurtherPosEvents = 2;
44
45 const char kXdndActionCopy[] = "XdndActionCopy";
46 const char kXdndActionMove[] = "XdndActionMove";
47 const char kXdndActionLink[] = "XdndActionLink";
48 const char kXdndActionDirectSave[] = "XdndActionDirectSave";
49
50 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
51 const char kXdndSelection[] = "XdndSelection";
52 const char kXdndDirectSave0[] = "XdndDirectSave0";
53
54 const char* kAtomsToCache[] = {
55   kChromiumDragReciever,
56   "XdndActionAsk",
57   kXdndActionCopy,
58   kXdndActionDirectSave,
59   kXdndActionLink,
60   "XdndActionList",
61   kXdndActionMove,
62   "XdndActionPrivate",
63   "XdndAware",
64   kXdndDirectSave0,
65   "XdndDrop",
66   "XdndEnter",
67   "XdndFinished",
68   "XdndLeave",
69   "XdndPosition",
70   "XdndProxy",  // Proxy windows?
71   kXdndSelection,
72   "XdndStatus",
73   "XdndTypeList",
74   ui::Clipboard::kMimeTypeText,
75   NULL
76 };
77
78 // The time to wait for the target to respond after the user has released the
79 // mouse button before ending the move loop.
80 const int kEndMoveLoopTimeoutMs = 1000;
81
82 // The time to wait since sending the last XdndPosition message before
83 // reprocessing the most recent mouse move event in case that the window
84 // stacking order has changed and |source_current_window_| needs to be updated.
85 const int kRepeatMouseMoveTimeoutMs = 350;
86
87 // The minimum alpha before we declare a pixel transparent when searching in
88 // our source image.
89 const uint32 kMinAlpha = 32;
90
91 // |drag_widget_|'s opacity.
92 const unsigned char kDragWidgetOpacity = 0xc0;
93
94 static base::LazyInstance<
95     std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
96         g_live_client_map = LAZY_INSTANCE_INITIALIZER;
97
98 }  // namespace
99
100 namespace views {
101
102 DesktopDragDropClientAuraX11*
103 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
104
105 class DesktopDragDropClientAuraX11::X11DragContext
106     : public ui::PlatformEventDispatcher {
107  public:
108   X11DragContext(ui::X11AtomCache* atom_cache,
109                  ::Window local_window,
110                  const XClientMessageEvent& event);
111   ~X11DragContext() override;
112
113   // When we receive an XdndPosition message, we need to have all the data
114   // copied from the other window before we process the XdndPosition
115   // message. If we have that data already, dispatch immediately. Otherwise,
116   // delay dispatching until we do.
117   void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
118                                   ::Atom suggested_action,
119                                   ::Window source_window,
120                                   const gfx::Point& screen_point);
121
122   // Called to request the next target from the source window. This is only
123   // done on the first XdndPosition; after that, we cache the data offered by
124   // the source window.
125   void RequestNextTarget();
126
127   // Called when XSelection data has been copied to our process.
128   void OnSelectionNotify(const XSelectionEvent& xselection);
129
130   // Clones the fetched targets.
131   const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
132
133   // Reads the "XdndActionList" property from |source_window| and copies it
134   // into |actions|.
135   void ReadActions();
136
137   // Creates a ui::DragDropTypes::DragOperation representation of the current
138   // action list.
139   int GetDragOperation() const;
140
141  private:
142   // Masks the X11 atom |xdnd_operation|'s views representation onto
143   // |drag_operation|.
144   void MaskOperation(::Atom xdnd_operation, int* drag_operation) const;
145
146   // ui::PlatformEventDispatcher:
147   bool CanDispatchEvent(const ui::PlatformEvent& event) override;
148   uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
149
150   // The atom cache owned by our parent.
151   ui::X11AtomCache* atom_cache_;
152
153   // The XID of our chrome local aura window handling our events.
154   ::Window local_window_;
155
156   // The XID of the window that's initiated the drag.
157   unsigned long source_window_;
158
159   // The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_|
160   // belongs to a Chrome window.
161   DesktopDragDropClientAuraX11* source_client_;
162
163   // Used to unselect PropertyChangeMask on |source_window_| if |source_window_|
164   // does not belong to a Chrome window when X11DragContext is destroyed.
165   int foreign_window_manager_source_window_id_;
166
167   // The client we inform once we're done with requesting data.
168   DesktopDragDropClientAuraX11* drag_drop_client_;
169
170   // Whether we're blocking the handling of an XdndPosition message by waiting
171   // for |unfetched_targets_| to be fetched.
172   bool waiting_to_handle_position_;
173
174   // Where the cursor is on screen.
175   gfx::Point screen_point_;
176
177   // A SelectionFormatMap of data that we have in our process.
178   ui::SelectionFormatMap fetched_targets_;
179
180   // The names of various data types offered by the other window that we
181   // haven't fetched and put in |fetched_targets_| yet.
182   std::vector<Atom> unfetched_targets_;
183
184   // XdndPosition messages have a suggested action. Qt applications exclusively
185   // use this, instead of the XdndActionList which is backed by |actions_|.
186   Atom suggested_action_;
187
188   // Possible actions.
189   std::vector<Atom> actions_;
190
191   DISALLOW_COPY_AND_ASSIGN(X11DragContext);
192 };
193
194 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
195     ui::X11AtomCache* atom_cache,
196     ::Window local_window,
197     const XClientMessageEvent& event)
198     : atom_cache_(atom_cache),
199       local_window_(local_window),
200       source_window_(event.data.l[0]),
201       source_client_(
202           DesktopDragDropClientAuraX11::GetForWindow(source_window_)),
203       foreign_window_manager_source_window_id_(0),
204       drag_drop_client_(NULL),
205       waiting_to_handle_position_(false),
206       suggested_action_(None) {
207   bool get_types = ((event.data.l[1] & 1) != 0);
208
209   if (get_types) {
210     if (!ui::GetAtomArrayProperty(source_window_,
211                                   "XdndTypeList",
212                                   &unfetched_targets_)) {
213       return;
214     }
215   } else {
216     // data.l[2,3,4] contain the first three types. Unused slots can be None.
217     for (int i = 0; i < 3; ++i) {
218       if (event.data.l[2+i] != None) {
219         unfetched_targets_.push_back(event.data.l[2+i]);
220       }
221     }
222   }
223
224   if (!source_client_) {
225     // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
226     // created by some other process. Listen for messages on it.
227     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
228     foreign_window_manager_source_window_id_ =
229         ui::XForeignWindowManager::GetInstance()->RequestEvents(
230             source_window_, PropertyChangeMask);
231
232     // We must perform a full sync here because we could be racing
233     // |source_window_|.
234     XSync(gfx::GetXDisplay(), False);
235   } else {
236     // This drag originates from an aura window within our process. This means
237     // that we can shortcut the X11 server and ask the owning SelectionOwner
238     // for the data it's offering.
239     fetched_targets_ = source_client_->GetFormatMap();
240     unfetched_targets_.clear();
241   }
242
243   ReadActions();
244 }
245
246 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
247   if (!source_client_) {
248     // Unsubscribe from message events.
249     ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
250     ui::XForeignWindowManager::GetInstance()->CancelRequest(
251         foreign_window_manager_source_window_id_);
252   }
253 }
254
255 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
256     DesktopDragDropClientAuraX11* client,
257     ::Atom suggested_action,
258     ::Window source_window,
259     const gfx::Point& screen_point) {
260   DCHECK_EQ(source_window_, source_window);
261   suggested_action_ = suggested_action;
262
263   if (!unfetched_targets_.empty()) {
264     // We have unfetched targets. That means we need to pause the handling of
265     // the position message and ask the other window for its data.
266     screen_point_ = screen_point;
267     drag_drop_client_ = client;
268     waiting_to_handle_position_ = true;
269
270     fetched_targets_ = ui::SelectionFormatMap();
271     RequestNextTarget();
272   } else {
273     client->CompleteXdndPosition(source_window, screen_point);
274   }
275 }
276
277 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
278   ::Atom target = unfetched_targets_.back();
279   unfetched_targets_.pop_back();
280
281   XConvertSelection(gfx::GetXDisplay(),
282                     atom_cache_->GetAtom(kXdndSelection),
283                     target,
284                     atom_cache_->GetAtom(kChromiumDragReciever),
285                     local_window_,
286                     CurrentTime);
287 }
288
289 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
290     const XSelectionEvent& event) {
291   if (!waiting_to_handle_position_) {
292     // A misbehaved window may send SelectionNotify without us requesting data
293     // via XConvertSelection().
294     return;
295   }
296   DCHECK(drag_drop_client_);
297   DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
298
299   scoped_refptr<base::RefCountedMemory> data;
300   ::Atom type = None;
301   if (ui::GetRawBytesOfProperty(local_window_, event.property,
302                                 &data, NULL, &type)) {
303     fetched_targets_.Insert(event.target, data);
304   }
305
306   if (!unfetched_targets_.empty()) {
307     RequestNextTarget();
308   } else {
309     waiting_to_handle_position_ = false;
310     drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
311     drag_drop_client_ = NULL;
312   }
313 }
314
315 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
316   if (!source_client_) {
317     std::vector<Atom> atom_array;
318     if (!ui::GetAtomArrayProperty(source_window_,
319                                   "XdndActionList",
320                                   &atom_array)) {
321       actions_.clear();
322     } else {
323       actions_.swap(atom_array);
324     }
325   } else {
326     // We have a property notify set up for other windows in case they change
327     // their action list. Thankfully, the views interface is static and you
328     // can't change the action list after you enter StartDragAndDrop().
329     actions_ = source_client_->GetOfferedDragOperations();
330   }
331 }
332
333 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
334   int drag_operation = ui::DragDropTypes::DRAG_NONE;
335   for (std::vector<Atom>::const_iterator it = actions_.begin();
336        it != actions_.end(); ++it) {
337     MaskOperation(*it, &drag_operation);
338   }
339
340   MaskOperation(suggested_action_, &drag_operation);
341
342   return drag_operation;
343 }
344
345 void DesktopDragDropClientAuraX11::X11DragContext::MaskOperation(
346     ::Atom xdnd_operation,
347     int* drag_operation) const {
348   if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
349     *drag_operation |= ui::DragDropTypes::DRAG_COPY;
350   else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
351     *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
352   else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
353     *drag_operation |= ui::DragDropTypes::DRAG_LINK;
354 }
355
356 bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
357     const ui::PlatformEvent& event) {
358   return event->xany.window == source_window_;
359 }
360
361 uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
362     const ui::PlatformEvent& event) {
363   if (event->type == PropertyNotify &&
364       event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
365     ReadActions();
366     return ui::POST_DISPATCH_STOP_PROPAGATION;
367   }
368   return ui::POST_DISPATCH_NONE;
369 }
370
371 ///////////////////////////////////////////////////////////////////////////////
372
373 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
374     aura::Window* root_window,
375     views::DesktopNativeCursorManager* cursor_manager,
376     Display* xdisplay,
377     ::Window xwindow)
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   // Some tests change the DesktopDragDropClientAuraX11 associated with an
395   // |xwindow|.
396   g_live_client_map.Get()[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::Init() {
424   move_loop_ = CreateMoveLoop(this);
425 }
426
427 void DesktopDragDropClientAuraX11::OnXdndEnter(
428     const XClientMessageEvent& event) {
429   DVLOG(1) << "XdndEnter";
430
431   int version = (event.data.l[1] & 0xff000000) >> 24;
432   if (version < 3) {
433     LOG(ERROR) << "Received old XdndEnter message.";
434     return;
435   }
436
437   // Make sure that we've run ~X11DragContext() before creating another one.
438   target_current_context_.reset();
439   target_current_context_.reset(
440       new X11DragContext(&atom_cache_, xwindow_, event));
441
442   // In the Windows implementation, we immediately call DesktopDropTargetWin::
443   // Translate(). Here, we wait until we receive an XdndPosition message
444   // because the enter message doesn't convey any positioning
445   // information.
446 }
447
448 void DesktopDragDropClientAuraX11::OnXdndLeave(
449     const XClientMessageEvent& event) {
450   DVLOG(1) << "XdndLeave";
451   NotifyDragLeave();
452   target_current_context_.reset();
453 }
454
455 void DesktopDragDropClientAuraX11::OnXdndPosition(
456     const XClientMessageEvent& event) {
457   DVLOG(1) << "XdndPosition";
458
459   unsigned long source_window = event.data.l[0];
460   int x_root_window = event.data.l[2] >> 16;
461   int y_root_window = event.data.l[2] & 0xffff;
462   ::Atom suggested_action = event.data.l[4];
463
464   if (!target_current_context_.get()) {
465     NOTREACHED();
466     return;
467   }
468
469   // If we already have all the data from this drag, we complete it
470   // immediately.
471   target_current_context_->OnStartXdndPositionMessage(
472       this, suggested_action, source_window,
473       gfx::Point(x_root_window, y_root_window));
474 }
475
476 void DesktopDragDropClientAuraX11::OnXdndStatus(
477     const XClientMessageEvent& event) {
478   DVLOG(1) << "XdndStatus";
479
480   unsigned long source_window = event.data.l[0];
481
482   if (source_window != source_current_window_)
483     return;
484
485   if (source_state_ != SOURCE_STATE_PENDING_DROP &&
486       source_state_ != SOURCE_STATE_OTHER) {
487     return;
488   }
489
490   waiting_on_status_ = false;
491   status_received_since_enter_ = true;
492
493   if (event.data.l[1] & 1) {
494     ::Atom atom_operation = event.data.l[4];
495     negotiated_operation_ = AtomToDragOperation(atom_operation);
496   } else {
497     negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
498   }
499
500   if (source_state_ == SOURCE_STATE_PENDING_DROP) {
501     // We were waiting on the status message so we could send the XdndDrop.
502     if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
503       move_loop_->EndMoveLoop();
504       return;
505     }
506     source_state_ = SOURCE_STATE_DROPPED;
507     SendXdndDrop(source_window);
508     return;
509   }
510
511   switch (negotiated_operation_) {
512     case ui::DragDropTypes::DRAG_COPY:
513       move_loop_->UpdateCursor(copy_grab_cursor_);
514       break;
515     case ui::DragDropTypes::DRAG_MOVE:
516       move_loop_->UpdateCursor(move_grab_cursor_);
517       break;
518     default:
519       move_loop_->UpdateCursor(grab_cursor_);
520       break;
521   }
522
523   // Note: event.data.[2,3] specify a rectangle. It is a request by the other
524   // window to not send further XdndPosition messages while the cursor is
525   // within it. However, it is considered advisory and (at least according to
526   // the spec) the other side must handle further position messages within
527   // it. GTK+ doesn't bother with this, so neither should we.
528
529   if (next_position_message_.get()) {
530     // We were waiting on the status message so we could send off the next
531     // position message we queued up.
532     gfx::Point p = next_position_message_->first;
533     unsigned long event_time = next_position_message_->second;
534     next_position_message_.reset();
535
536     SendXdndPosition(source_window, p, event_time);
537   }
538 }
539
540 void DesktopDragDropClientAuraX11::OnXdndFinished(
541     const XClientMessageEvent& event) {
542   DVLOG(1) << "XdndFinished";
543   unsigned long source_window = event.data.l[0];
544   if (source_current_window_ != source_window)
545     return;
546
547   // Clear |negotiated_operation_| if the drag was rejected.
548   if ((event.data.l[1] & 1) == 0)
549     negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
550
551   // Clear |source_current_window_| to avoid sending XdndLeave upon ending the
552   // move loop.
553   source_current_window_ = None;
554   move_loop_->EndMoveLoop();
555 }
556
557 void DesktopDragDropClientAuraX11::OnXdndDrop(
558     const XClientMessageEvent& event) {
559   DVLOG(1) << "XdndDrop";
560
561   unsigned long source_window = event.data.l[0];
562
563   int drag_operation = ui::DragDropTypes::DRAG_NONE;
564   if (target_window_) {
565     aura::client::DragDropDelegate* delegate =
566         aura::client::GetDragDropDelegate(target_window_);
567     if (delegate) {
568       ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
569           xwindow_, target_current_context_->fetched_targets()));
570
571       ui::DropTargetEvent event(data,
572                                 target_window_location_,
573                                 target_window_root_location_,
574                                 target_current_context_->GetDragOperation());
575       drag_operation = delegate->OnPerformDrop(event);
576     }
577
578     target_window_->RemoveObserver(this);
579     target_window_ = NULL;
580   }
581
582   XEvent xev;
583   xev.xclient.type = ClientMessage;
584   xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
585   xev.xclient.format = 32;
586   xev.xclient.window = source_window;
587   xev.xclient.data.l[0] = xwindow_;
588   xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
589   xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
590
591   SendXClientEvent(source_window, &xev);
592 }
593
594 void DesktopDragDropClientAuraX11::OnSelectionNotify(
595     const XSelectionEvent& xselection) {
596   if (target_current_context_)
597     target_current_context_->OnSelectionNotify(xselection);
598
599   // ICCCM requires us to delete the property passed into SelectionNotify.
600   if (xselection.property != None)
601     XDeleteProperty(xdisplay_, xwindow_, xselection.property);
602 }
603
604 int DesktopDragDropClientAuraX11::StartDragAndDrop(
605     const ui::OSExchangeData& data,
606     aura::Window* root_window,
607     aura::Window* source_window,
608     const gfx::Point& root_location,
609     int operation,
610     ui::DragDropTypes::DragEventSource source) {
611   source_current_window_ = None;
612   DCHECK(!g_current_drag_drop_client);
613   g_current_drag_drop_client = this;
614   waiting_on_status_ = false;
615   next_position_message_.reset();
616   status_received_since_enter_ = false;
617   source_state_ = SOURCE_STATE_OTHER;
618   drag_operation_ = operation;
619   negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
620
621   const ui::OSExchangeData::Provider* provider = &data.provider();
622   source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
623       provider);
624
625   source_provider_->TakeOwnershipOfSelection();
626
627   std::vector< ::Atom> actions = GetOfferedDragOperations();
628   if (!source_provider_->file_contents_name().empty()) {
629     actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
630     ui::SetStringProperty(
631         xwindow_,
632         atom_cache_.GetAtom(kXdndDirectSave0),
633         atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
634         source_provider_->file_contents_name().AsUTF8Unsafe());
635   }
636   ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
637
638   gfx::ImageSkia drag_image = source_provider_->GetDragImage();
639   if (IsValidDragImage(drag_image)) {
640     CreateDragWidget(drag_image);
641     drag_widget_offset_ = source_provider_->GetDragImageOffset();
642   }
643
644   // Chrome expects starting drag and drop to release capture.
645   aura::Window* capture_window =
646       aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
647   if (capture_window)
648     capture_window->ReleaseCapture();
649
650   // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
651   // move loop, which would also destroy this drag-client. So keep track of
652   // whether it is alive after the drag ends.
653   base::WeakPtr<DesktopDragDropClientAuraX11> alive(
654       weak_ptr_factory_.GetWeakPtr());
655
656   // Windows has a specific method, DoDragDrop(), which performs the entire
657   // drag. We have to emulate this, so we spin off a nested runloop which will
658   // track all cursor movement and reroute events to a specific handler.
659   move_loop_->RunMoveLoop(source_window, grab_cursor_);
660
661   if (alive) {
662     drag_widget_.reset();
663
664     source_provider_ = NULL;
665     g_current_drag_drop_client = NULL;
666     drag_operation_ = 0;
667     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
668     XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
669
670     return negotiated_operation_;
671   }
672   return ui::DragDropTypes::DRAG_NONE;
673 }
674
675 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
676                                               const ui::LocatedEvent& event) {
677   NOTIMPLEMENTED();
678 }
679
680 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
681                                         const ui::LocatedEvent& event) {
682   NOTIMPLEMENTED();
683 }
684
685 void DesktopDragDropClientAuraX11::DragCancel() {
686   move_loop_->EndMoveLoop();
687 }
688
689 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
690   return !!g_current_drag_drop_client;
691 }
692
693 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
694   DCHECK_EQ(target_window_, window);
695   target_window_ = NULL;
696 }
697
698 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
699   gfx::Point screen_point(event->x_root, event->y_root);
700   if (drag_widget_.get()) {
701     drag_widget_->SetBounds(
702         gfx::Rect(screen_point - drag_widget_offset_,
703                   drag_widget_->GetWindowBoundsInScreen().size()));
704     drag_widget_->StackAtTop();
705   }
706
707   repeat_mouse_move_timer_.Stop();
708   ProcessMouseMove(screen_point, event->time);
709 }
710
711 void DesktopDragDropClientAuraX11::OnMouseReleased() {
712   repeat_mouse_move_timer_.Stop();
713
714   if (source_state_ != SOURCE_STATE_OTHER) {
715     // The user has previously released the mouse and is clicking in
716     // frustration.
717     move_loop_->EndMoveLoop();
718     return;
719   }
720
721   if (source_current_window_ != None) {
722     if (waiting_on_status_) {
723       if (status_received_since_enter_) {
724         // If we are waiting for an XdndStatus message, we need to wait for it
725         // to complete.
726         source_state_ = SOURCE_STATE_PENDING_DROP;
727
728         // Start timer to end the move loop if the target takes too long to send
729         // the XdndStatus and XdndFinished messages.
730         StartEndMoveLoopTimer();
731         return;
732       }
733
734       move_loop_->EndMoveLoop();
735       return;
736     }
737
738     if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) {
739       // Start timer to end the move loop if the target takes too long to send
740       // an XdndFinished message. It is important that StartEndMoveLoopTimer()
741       // is called before SendXdndDrop() because SendXdndDrop()
742       // sends XdndFinished synchronously if the drop target is a Chrome
743       // window.
744       StartEndMoveLoopTimer();
745
746       // We have negotiated an action with the other end.
747       source_state_ = SOURCE_STATE_DROPPED;
748       SendXdndDrop(source_current_window_);
749       return;
750     }
751   }
752
753   move_loop_->EndMoveLoop();
754 }
755
756 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
757   if (source_current_window_ != None) {
758     SendXdndLeave(source_current_window_);
759     source_current_window_ = None;
760   }
761   target_current_context_.reset();
762   repeat_mouse_move_timer_.Stop();
763   end_move_loop_timer_.Stop();
764 }
765
766 scoped_ptr<X11MoveLoop> DesktopDragDropClientAuraX11::CreateMoveLoop(
767     X11MoveLoopDelegate* delegate) {
768   return scoped_ptr<X11MoveLoop>(new X11WholeScreenMoveLoop(this));
769 }
770
771 XID DesktopDragDropClientAuraX11::FindWindowFor(
772     const gfx::Point& screen_point) {
773   views::X11TopmostWindowFinder finder;
774   ::Window target = finder.FindWindowAt(screen_point);
775
776   if (target == None)
777     return None;
778
779   // Figure out which window we should test as XdndAware. If |target| has
780   // XdndProxy, it will set that proxy on target, and if not, |target|'s
781   // original value will remain.
782   ui::GetXIDProperty(target, "XdndProxy", &target);
783
784   int version;
785   if (ui::GetIntProperty(target, "XdndAware", &version) &&
786       version >= kMinXdndVersion) {
787     return target;
788   }
789   return None;
790 }
791
792 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
793                                                     XEvent* xev) {
794   DCHECK_EQ(ClientMessage, xev->type);
795
796   // Don't send messages to the X11 message queue if we can help it.
797   DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
798   if (short_circuit) {
799     Atom message_type = xev->xclient.message_type;
800     if (message_type == atom_cache_.GetAtom("XdndEnter")) {
801       short_circuit->OnXdndEnter(xev->xclient);
802       return;
803     } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
804       short_circuit->OnXdndLeave(xev->xclient);
805       return;
806     } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
807       short_circuit->OnXdndPosition(xev->xclient);
808       return;
809     } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
810       short_circuit->OnXdndStatus(xev->xclient);
811       return;
812     } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
813       short_circuit->OnXdndFinished(xev->xclient);
814       return;
815     } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
816       short_circuit->OnXdndDrop(xev->xclient);
817       return;
818     }
819   }
820
821   // I don't understand why the GTK+ code is doing what it's doing here. It
822   // goes out of its way to send the XEvent so that it receives a callback on
823   // success or failure, and when it fails, it then sends an internal
824   // GdkEvent about the failed drag. (And sending this message doesn't appear
825   // to go through normal xlib machinery, but instead passes through the low
826   // level xProto (the x11 wire format) that I don't understand.
827   //
828   // I'm unsure if I have to jump through those hoops, or if XSendEvent is
829   // sufficient.
830   XSendEvent(xdisplay_, xid, False, 0, xev);
831 }
832
833 void DesktopDragDropClientAuraX11::ProcessMouseMove(
834     const gfx::Point& screen_point,
835     unsigned long event_time) {
836   if (source_state_ != SOURCE_STATE_OTHER)
837     return;
838
839   // Find the current window the cursor is over.
840   ::Window dest_window = FindWindowFor(screen_point);
841
842   if (source_current_window_ != dest_window) {
843     if (source_current_window_ != None)
844       SendXdndLeave(source_current_window_);
845
846     source_current_window_ = dest_window;
847     waiting_on_status_ = false;
848     next_position_message_.reset();
849     status_received_since_enter_ = false;
850     negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
851
852     if (source_current_window_ != None)
853       SendXdndEnter(source_current_window_);
854   }
855
856   if (source_current_window_ != None) {
857     if (waiting_on_status_) {
858       next_position_message_.reset(
859           new std::pair<gfx::Point, unsigned long>(screen_point, event_time));
860     } else {
861       SendXdndPosition(dest_window, screen_point, event_time);
862     }
863   }
864 }
865
866 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
867   end_move_loop_timer_.Start(FROM_HERE,
868                              base::TimeDelta::FromMilliseconds(
869                                  kEndMoveLoopTimeoutMs),
870                              this,
871                              &DesktopDragDropClientAuraX11::EndMoveLoop);
872 }
873
874 void DesktopDragDropClientAuraX11::EndMoveLoop() {
875   move_loop_->EndMoveLoop();
876 }
877
878 void DesktopDragDropClientAuraX11::DragTranslate(
879     const gfx::Point& root_window_location,
880     scoped_ptr<ui::OSExchangeData>* data,
881     scoped_ptr<ui::DropTargetEvent>* event,
882     aura::client::DragDropDelegate** delegate) {
883   gfx::Point root_location = root_window_location;
884   root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
885   aura::Window* target_window =
886       root_window_->GetEventHandlerForPoint(root_location);
887   bool target_window_changed = false;
888   if (target_window != target_window_) {
889     if (target_window_)
890       NotifyDragLeave();
891     target_window_ = target_window;
892     if (target_window_)
893       target_window_->AddObserver(this);
894     target_window_changed = true;
895   }
896   *delegate = NULL;
897   if (!target_window_)
898     return;
899   *delegate = aura::client::GetDragDropDelegate(target_window_);
900   if (!*delegate)
901     return;
902
903   data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
904       xwindow_, target_current_context_->fetched_targets())));
905   gfx::Point location = root_location;
906   aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
907
908   target_window_location_ = location;
909   target_window_root_location_ = root_location;
910
911   int drag_op = target_current_context_->GetDragOperation();
912   // KDE-based file browsers such as Dolphin change the drag operation depending
913   // on whether alt/ctrl/shift was pressed. However once Chromium gets control
914   // over the X11 events, the source application does no longer receive X11
915   // events for key modifier changes, so the dnd operation gets stuck in an
916   // incorrect state. Blink can only dnd-open files of type DRAG_COPY, so the
917   // DRAG_COPY mask is added if the dnd object is a file.
918   if (drag_op & (ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_LINK) &&
919       data->get()->HasFile()) {
920     drag_op |= ui::DragDropTypes::DRAG_COPY;
921   }
922
923   event->reset(new ui::DropTargetEvent(
924       *(data->get()),
925       location,
926       root_location,
927       drag_op));
928   if (target_window_changed)
929     (*delegate)->OnDragEntered(*event->get());
930 }
931
932 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
933   if (!target_window_)
934     return;
935   DragDropDelegate* delegate =
936       aura::client::GetDragDropDelegate(target_window_);
937   if (delegate)
938     delegate->OnDragExited();
939   target_window_->RemoveObserver(this);
940   target_window_ = NULL;
941 }
942
943 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
944     int drag_operation) {
945   if (drag_operation & ui::DragDropTypes::DRAG_COPY)
946     return atom_cache_.GetAtom(kXdndActionCopy);
947   if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
948     return atom_cache_.GetAtom(kXdndActionMove);
949   if (drag_operation & ui::DragDropTypes::DRAG_LINK)
950     return atom_cache_.GetAtom(kXdndActionLink);
951
952   return None;
953 }
954
955 ui::DragDropTypes::DragOperation
956 DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
957   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
958     return ui::DragDropTypes::DRAG_COPY;
959   if (atom == atom_cache_.GetAtom(kXdndActionMove))
960     return ui::DragDropTypes::DRAG_MOVE;
961   if (atom == atom_cache_.GetAtom(kXdndActionLink))
962     return ui::DragDropTypes::DRAG_LINK;
963
964   return ui::DragDropTypes::DRAG_NONE;
965 }
966
967 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
968   std::vector< ::Atom> operations;
969   if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
970     operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
971   if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
972     operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
973   if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
974     operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
975   return operations;
976 }
977
978 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
979   return source_provider_ ? source_provider_->GetFormatMap() :
980       ui::SelectionFormatMap();
981 }
982
983 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
984     ::Window source_window,
985     const gfx::Point& screen_point) {
986   int drag_operation = ui::DragDropTypes::DRAG_NONE;
987   scoped_ptr<ui::OSExchangeData> data;
988   scoped_ptr<ui::DropTargetEvent> drop_target_event;
989   DragDropDelegate* delegate = NULL;
990   DragTranslate(screen_point, &data, &drop_target_event, &delegate);
991   if (delegate)
992     drag_operation = delegate->OnDragUpdated(*drop_target_event);
993
994   // Sends an XdndStatus message back to the source_window. l[2,3]
995   // theoretically represent an area in the window where the current action is
996   // the same as what we're returning, but I can't find any implementation that
997   // actually making use of this. A client can return (0, 0) and/or set the
998   // first bit of l[1] to disable the feature, and it appears that gtk neither
999   // sets this nor respects it if set.
1000   XEvent xev;
1001   xev.xclient.type = ClientMessage;
1002   xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
1003   xev.xclient.format = 32;
1004   xev.xclient.window = source_window;
1005   xev.xclient.data.l[0] = xwindow_;
1006   xev.xclient.data.l[1] = (drag_operation != 0) ?
1007       (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
1008   xev.xclient.data.l[2] = 0;
1009   xev.xclient.data.l[3] = 0;
1010   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
1011
1012   SendXClientEvent(source_window, &xev);
1013 }
1014
1015 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
1016   XEvent xev;
1017   xev.xclient.type = ClientMessage;
1018   xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
1019   xev.xclient.format = 32;
1020   xev.xclient.window = dest_window;
1021   xev.xclient.data.l[0] = xwindow_;
1022   xev.xclient.data.l[1] = (kMinXdndVersion << 24);  // The version number.
1023   xev.xclient.data.l[2] = 0;
1024   xev.xclient.data.l[3] = 0;
1025   xev.xclient.data.l[4] = 0;
1026
1027   std::vector<Atom> targets;
1028   source_provider_->RetrieveTargets(&targets);
1029
1030   if (targets.size() > 3) {
1031     xev.xclient.data.l[1] |= 1;
1032     ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
1033   } else {
1034     // Pack the targets into the enter message.
1035     for (size_t i = 0; i < targets.size(); ++i)
1036       xev.xclient.data.l[2 + i] = targets[i];
1037   }
1038
1039   SendXClientEvent(dest_window, &xev);
1040 }
1041
1042 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
1043   XEvent xev;
1044   xev.xclient.type = ClientMessage;
1045   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
1046   xev.xclient.format = 32;
1047   xev.xclient.window = dest_window;
1048   xev.xclient.data.l[0] = xwindow_;
1049   xev.xclient.data.l[1] = 0;
1050   xev.xclient.data.l[2] = 0;
1051   xev.xclient.data.l[3] = 0;
1052   xev.xclient.data.l[4] = 0;
1053   SendXClientEvent(dest_window, &xev);
1054 }
1055
1056 void DesktopDragDropClientAuraX11::SendXdndPosition(
1057     ::Window dest_window,
1058     const gfx::Point& screen_point,
1059     unsigned long event_time) {
1060   waiting_on_status_ = true;
1061
1062   XEvent xev;
1063   xev.xclient.type = ClientMessage;
1064   xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
1065   xev.xclient.format = 32;
1066   xev.xclient.window = dest_window;
1067   xev.xclient.data.l[0] = xwindow_;
1068   xev.xclient.data.l[1] = 0;
1069   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
1070   xev.xclient.data.l[3] = event_time;
1071   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
1072   SendXClientEvent(dest_window, &xev);
1073
1074   // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
1075   // the Xdnd protocol both recommend that drag events should be sent
1076   // periodically.
1077   repeat_mouse_move_timer_.Start(
1078       FROM_HERE,
1079       base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
1080       base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
1081                  base::Unretained(this),
1082                  screen_point,
1083                  event_time));
1084 }
1085
1086 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
1087   XEvent xev;
1088   xev.xclient.type = ClientMessage;
1089   xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
1090   xev.xclient.format = 32;
1091   xev.xclient.window = dest_window;
1092   xev.xclient.data.l[0] = xwindow_;
1093   xev.xclient.data.l[1] = 0;
1094   xev.xclient.data.l[2] = CurrentTime;
1095   xev.xclient.data.l[3] = None;
1096   xev.xclient.data.l[4] = None;
1097   SendXClientEvent(dest_window, &xev);
1098 }
1099
1100 void DesktopDragDropClientAuraX11::CreateDragWidget(
1101     const gfx::ImageSkia& image) {
1102   Widget* widget = new Widget;
1103   Widget::InitParams params(Widget::InitParams::TYPE_DRAG);
1104   params.opacity = Widget::InitParams::OPAQUE_WINDOW;
1105   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1106   params.accept_events = false;
1107
1108   gfx::Point location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint() -
1109                         drag_widget_offset_;
1110   params.bounds = gfx::Rect(location, image.size());
1111   widget->set_focus_on_creation(false);
1112   widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE);
1113   widget->Init(params);
1114   widget->SetOpacity(kDragWidgetOpacity);
1115   widget->GetNativeWindow()->SetName("DragWindow");
1116
1117   ImageView* image_view = new ImageView();
1118   image_view->SetImage(image);
1119   image_view->SetBounds(0, 0, image.width(), image.height());
1120   widget->SetContentsView(image_view);
1121   widget->Show();
1122   widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false);
1123
1124   drag_widget_.reset(widget);
1125 }
1126
1127 bool DesktopDragDropClientAuraX11::IsValidDragImage(
1128     const gfx::ImageSkia& image) {
1129   if (image.isNull())
1130     return false;
1131
1132   // Because we need a GL context per window, we do a quick check so that we
1133   // don't make another context if the window would just be displaying a mostly
1134   // transparent image.
1135   const SkBitmap* in_bitmap = image.bitmap();
1136   SkAutoLockPixels in_lock(*in_bitmap);
1137   for (int y = 0; y < in_bitmap->height(); ++y) {
1138     uint32* in_row = in_bitmap->getAddr32(0, y);
1139
1140     for (int x = 0; x < in_bitmap->width(); ++x) {
1141       if (SkColorGetA(in_row[x]) > kMinAlpha)
1142         return true;
1143     }
1144   }
1145
1146   return false;
1147 }
1148
1149 }  // namespace views