Android: Get rid of extra dup()s on launching child processes
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11.cc
blob70db0361e44eb8c6fba012ca33d9883bb73f252c
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.
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
7 #include <X11/Xatom.h>
9 #include "base/event_types.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "ui/aura/client/capture_client.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_tree_host.h"
17 #include "ui/base/clipboard/clipboard.h"
18 #include "ui/base/dragdrop/drop_target_event.h"
19 #include "ui/base/dragdrop/os_exchange_data.h"
20 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
21 #include "ui/base/x/selection_utils.h"
22 #include "ui/base/x/x11_foreign_window_manager.h"
23 #include "ui/base/x/x11_util.h"
24 #include "ui/events/event.h"
25 #include "ui/events/event_utils.h"
26 #include "ui/events/platform/platform_event_source.h"
27 #include "ui/gfx/image/image_skia.h"
28 #include "ui/gfx/screen.h"
29 #include "ui/views/controls/image_view.h"
30 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
31 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
32 #include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h"
33 #include "ui/views/widget/widget.h"
34 #include "ui/wm/public/drag_drop_client.h"
35 #include "ui/wm/public/drag_drop_delegate.h"
37 using aura::client::DragDropDelegate;
38 using ui::OSExchangeData;
40 namespace {
42 const int kMinXdndVersion = 5;
44 const int kWillAcceptDrop = 1;
45 const int kWantFurtherPosEvents = 2;
47 const char kXdndActionCopy[] = "XdndActionCopy";
48 const char kXdndActionMove[] = "XdndActionMove";
49 const char kXdndActionLink[] = "XdndActionLink";
50 const char kXdndActionDirectSave[] = "XdndActionDirectSave";
52 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
53 const char kXdndSelection[] = "XdndSelection";
54 const char kXdndDirectSave0[] = "XdndDirectSave0";
56 const char* kAtomsToCache[] = {
57 kChromiumDragReciever,
58 "XdndActionAsk",
59 kXdndActionCopy,
60 kXdndActionDirectSave,
61 kXdndActionLink,
62 "XdndActionList",
63 kXdndActionMove,
64 "XdndActionPrivate",
65 "XdndAware",
66 kXdndDirectSave0,
67 "XdndDrop",
68 "XdndEnter",
69 "XdndFinished",
70 "XdndLeave",
71 "XdndPosition",
72 "XdndProxy", // Proxy windows?
73 kXdndSelection,
74 "XdndStatus",
75 "XdndTypeList",
76 ui::Clipboard::kMimeTypeText,
77 NULL
80 int XGetModifiers() {
81 XDisplay* display = gfx::GetXDisplay();
83 XID root, child;
84 int root_x, root_y, win_x, win_y;
85 unsigned int mask;
86 XQueryPointer(display,
87 DefaultRootWindow(display),
88 &root,
89 &child,
90 &root_x,
91 &root_y,
92 &win_x,
93 &win_y,
94 &mask);
95 int modifiers = ui::EF_NONE;
96 if (mask & ShiftMask)
97 modifiers |= ui::EF_SHIFT_DOWN;
98 if (mask & ControlMask)
99 modifiers |= ui::EF_CONTROL_DOWN;
100 if (mask & Mod1Mask)
101 modifiers |= ui::EF_ALT_DOWN;
102 if (mask & Mod4Mask)
103 modifiers |= ui::EF_COMMAND_DOWN;
104 return modifiers;
107 // The time to wait for the target to respond after the user has released the
108 // mouse button before ending the move loop.
109 const int kEndMoveLoopTimeoutMs = 1000;
111 // The time to wait since sending the last XdndPosition message before
112 // reprocessing the most recent mouse move event in case that the window
113 // stacking order has changed and |source_current_window_| needs to be updated.
114 const int kRepeatMouseMoveTimeoutMs = 350;
116 // The minimum alpha before we declare a pixel transparent when searching in
117 // our source image.
118 const uint32 kMinAlpha = 32;
120 // |drag_widget_|'s opacity.
121 const unsigned char kDragWidgetOpacity = 0xc0;
123 static base::LazyInstance<
124 std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
125 g_live_client_map = LAZY_INSTANCE_INITIALIZER;
127 } // namespace
129 namespace views {
131 DesktopDragDropClientAuraX11*
132 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
134 class DesktopDragDropClientAuraX11::X11DragContext
135 : public ui::PlatformEventDispatcher {
136 public:
137 X11DragContext(ui::X11AtomCache* atom_cache,
138 ::Window local_window,
139 const XClientMessageEvent& event);
140 ~X11DragContext() override;
142 // When we receive an XdndPosition message, we need to have all the data
143 // copied from the other window before we process the XdndPosition
144 // message. If we have that data already, dispatch immediately. Otherwise,
145 // delay dispatching until we do.
146 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
147 ::Atom suggested_action,
148 ::Window source_window,
149 const gfx::Point& screen_point);
151 // Called to request the next target from the source window. This is only
152 // done on the first XdndPosition; after that, we cache the data offered by
153 // the source window.
154 void RequestNextTarget();
156 // Called when XSelection data has been copied to our process.
157 void OnSelectionNotify(const XSelectionEvent& xselection);
159 // Clones the fetched targets.
160 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
162 // Reads the "XdndActionList" property from |source_window| and copies it
163 // into |actions|.
164 void ReadActions();
166 // Creates a ui::DragDropTypes::DragOperation representation of the current
167 // action list.
168 int GetDragOperation() const;
170 DesktopDragDropClientAuraX11* source_client() { return source_client_; }
172 private:
173 // Masks the X11 atom |xdnd_operation|'s views representation onto
174 // |drag_operation|.
175 void MaskOperation(::Atom xdnd_operation, int* drag_operation) const;
177 // ui::PlatformEventDispatcher:
178 bool CanDispatchEvent(const ui::PlatformEvent& event) override;
179 uint32_t DispatchEvent(const ui::PlatformEvent& event) override;
181 // The atom cache owned by our parent.
182 ui::X11AtomCache* atom_cache_;
184 // The XID of our chrome local aura window handling our events.
185 ::Window local_window_;
187 // The XID of the window that's initiated the drag.
188 unsigned long source_window_;
190 // The DesktopDragDropClientAuraX11 for |source_window_| if |source_window_|
191 // belongs to a Chrome window.
192 DesktopDragDropClientAuraX11* source_client_;
194 // Used to unselect PropertyChangeMask on |source_window_| if |source_window_|
195 // does not belong to a Chrome window when X11DragContext is destroyed.
196 int foreign_window_manager_source_window_id_;
198 // The client we inform once we're done with requesting data.
199 DesktopDragDropClientAuraX11* drag_drop_client_;
201 // Whether we're blocking the handling of an XdndPosition message by waiting
202 // for |unfetched_targets_| to be fetched.
203 bool waiting_to_handle_position_;
205 // Where the cursor is on screen.
206 gfx::Point screen_point_;
208 // A SelectionFormatMap of data that we have in our process.
209 ui::SelectionFormatMap fetched_targets_;
211 // The names of various data types offered by the other window that we
212 // haven't fetched and put in |fetched_targets_| yet.
213 std::vector<Atom> unfetched_targets_;
215 // XdndPosition messages have a suggested action. Qt applications exclusively
216 // use this, instead of the XdndActionList which is backed by |actions_|.
217 Atom suggested_action_;
219 // Possible actions.
220 std::vector<Atom> actions_;
222 DISALLOW_COPY_AND_ASSIGN(X11DragContext);
225 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
226 ui::X11AtomCache* atom_cache,
227 ::Window local_window,
228 const XClientMessageEvent& event)
229 : atom_cache_(atom_cache),
230 local_window_(local_window),
231 source_window_(event.data.l[0]),
232 source_client_(
233 DesktopDragDropClientAuraX11::GetForWindow(source_window_)),
234 foreign_window_manager_source_window_id_(0),
235 drag_drop_client_(NULL),
236 waiting_to_handle_position_(false),
237 suggested_action_(None) {
238 if (!source_client_) {
239 bool get_types = ((event.data.l[1] & 1) != 0);
241 if (get_types) {
242 if (!ui::GetAtomArrayProperty(source_window_,
243 "XdndTypeList",
244 &unfetched_targets_)) {
245 return;
247 } else {
248 // data.l[2,3,4] contain the first three types. Unused slots can be None.
249 for (int i = 0; i < 3; ++i) {
250 if (event.data.l[2+i] != None) {
251 unfetched_targets_.push_back(event.data.l[2+i]);
256 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
257 // created by some other process. Listen for messages on it.
258 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
259 foreign_window_manager_source_window_id_ =
260 ui::XForeignWindowManager::GetInstance()->RequestEvents(
261 source_window_, PropertyChangeMask);
263 // We must perform a full sync here because we could be racing
264 // |source_window_|.
265 XSync(gfx::GetXDisplay(), False);
266 } else {
267 // This drag originates from an aura window within our process. This means
268 // that we can shortcut the X11 server and ask the owning SelectionOwner
269 // for the data it's offering.
270 fetched_targets_ = source_client_->GetFormatMap();
273 ReadActions();
276 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
277 if (!source_client_) {
278 // Unsubscribe from message events.
279 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
280 ui::XForeignWindowManager::GetInstance()->CancelRequest(
281 foreign_window_manager_source_window_id_);
285 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
286 DesktopDragDropClientAuraX11* client,
287 ::Atom suggested_action,
288 ::Window source_window,
289 const gfx::Point& screen_point) {
290 DCHECK_EQ(source_window_, source_window);
291 suggested_action_ = suggested_action;
293 if (!unfetched_targets_.empty()) {
294 // We have unfetched targets. That means we need to pause the handling of
295 // the position message and ask the other window for its data.
296 screen_point_ = screen_point;
297 drag_drop_client_ = client;
298 waiting_to_handle_position_ = true;
300 fetched_targets_ = ui::SelectionFormatMap();
301 RequestNextTarget();
302 } else {
303 client->CompleteXdndPosition(source_window, screen_point);
307 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
308 ::Atom target = unfetched_targets_.back();
309 unfetched_targets_.pop_back();
311 XConvertSelection(gfx::GetXDisplay(),
312 atom_cache_->GetAtom(kXdndSelection),
313 target,
314 atom_cache_->GetAtom(kChromiumDragReciever),
315 local_window_,
316 CurrentTime);
319 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
320 const XSelectionEvent& event) {
321 if (!waiting_to_handle_position_) {
322 // A misbehaved window may send SelectionNotify without us requesting data
323 // via XConvertSelection().
324 return;
326 DCHECK(drag_drop_client_);
327 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
329 scoped_refptr<base::RefCountedMemory> data;
330 ::Atom type = None;
331 if (ui::GetRawBytesOfProperty(local_window_, event.property,
332 &data, NULL, &type)) {
333 fetched_targets_.Insert(event.target, data);
336 if (!unfetched_targets_.empty()) {
337 RequestNextTarget();
338 } else {
339 waiting_to_handle_position_ = false;
340 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
341 drag_drop_client_ = NULL;
345 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
346 if (!source_client_) {
347 std::vector<Atom> atom_array;
348 if (!ui::GetAtomArrayProperty(source_window_,
349 "XdndActionList",
350 &atom_array)) {
351 actions_.clear();
352 } else {
353 actions_.swap(atom_array);
355 } else {
356 // We have a property notify set up for other windows in case they change
357 // their action list. Thankfully, the views interface is static and you
358 // can't change the action list after you enter StartDragAndDrop().
359 actions_ = source_client_->GetOfferedDragOperations();
363 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
364 int drag_operation = ui::DragDropTypes::DRAG_NONE;
365 for (std::vector<Atom>::const_iterator it = actions_.begin();
366 it != actions_.end(); ++it) {
367 MaskOperation(*it, &drag_operation);
370 MaskOperation(suggested_action_, &drag_operation);
372 return drag_operation;
375 void DesktopDragDropClientAuraX11::X11DragContext::MaskOperation(
376 ::Atom xdnd_operation,
377 int* drag_operation) const {
378 if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
379 *drag_operation |= ui::DragDropTypes::DRAG_COPY;
380 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
381 *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
382 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
383 *drag_operation |= ui::DragDropTypes::DRAG_LINK;
386 bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
387 const ui::PlatformEvent& event) {
388 return event->xany.window == source_window_;
391 uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
392 const ui::PlatformEvent& event) {
393 if (event->type == PropertyNotify &&
394 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
395 ReadActions();
396 return ui::POST_DISPATCH_STOP_PROPAGATION;
398 return ui::POST_DISPATCH_NONE;
401 ///////////////////////////////////////////////////////////////////////////////
403 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
404 aura::Window* root_window,
405 views::DesktopNativeCursorManager* cursor_manager,
406 Display* xdisplay,
407 ::Window xwindow)
408 : root_window_(root_window),
409 cursor_manager_(cursor_manager),
410 xdisplay_(xdisplay),
411 xwindow_(xwindow),
412 atom_cache_(xdisplay_, kAtomsToCache),
413 current_modifier_state_(ui::EF_NONE),
414 target_window_(NULL),
415 waiting_on_status_(false),
416 status_received_since_enter_(false),
417 source_provider_(NULL),
418 source_current_window_(None),
419 source_state_(SOURCE_STATE_OTHER),
420 drag_operation_(0),
421 negotiated_operation_(ui::DragDropTypes::DRAG_NONE),
422 weak_ptr_factory_(this) {
423 // Some tests change the DesktopDragDropClientAuraX11 associated with an
424 // |xwindow|.
425 g_live_client_map.Get()[xwindow] = this;
427 // Mark that we are aware of drag and drop concepts.
428 unsigned long xdnd_version = kMinXdndVersion;
429 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
430 XA_ATOM, 32, PropModeReplace,
431 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
434 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
435 // This is necessary when the parent native widget gets destroyed while a drag
436 // operation is in progress.
437 move_loop_->EndMoveLoop();
438 NotifyDragLeave();
440 g_live_client_map.Get().erase(xwindow_);
443 // static
444 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
445 ::Window window) {
446 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
447 g_live_client_map.Get().find(window);
448 if (it == g_live_client_map.Get().end())
449 return NULL;
450 return it->second;
453 void DesktopDragDropClientAuraX11::Init() {
454 move_loop_ = CreateMoveLoop(this);
457 void DesktopDragDropClientAuraX11::OnXdndEnter(
458 const XClientMessageEvent& event) {
459 DVLOG(1) << "XdndEnter";
461 int version = (event.data.l[1] & 0xff000000) >> 24;
462 if (version < 3) {
463 LOG(ERROR) << "Received old XdndEnter message.";
464 return;
467 // Make sure that we've run ~X11DragContext() before creating another one.
468 target_current_context_.reset();
469 target_current_context_.reset(
470 new X11DragContext(&atom_cache_, xwindow_, event));
472 // In the Windows implementation, we immediately call DesktopDropTargetWin::
473 // Translate(). Here, we wait until we receive an XdndPosition message
474 // because the enter message doesn't convey any positioning
475 // information.
478 void DesktopDragDropClientAuraX11::OnXdndLeave(
479 const XClientMessageEvent& event) {
480 DVLOG(1) << "XdndLeave";
481 NotifyDragLeave();
482 target_current_context_.reset();
485 void DesktopDragDropClientAuraX11::OnXdndPosition(
486 const XClientMessageEvent& event) {
487 DVLOG(1) << "XdndPosition";
489 unsigned long source_window = event.data.l[0];
490 int x_root_window = event.data.l[2] >> 16;
491 int y_root_window = event.data.l[2] & 0xffff;
492 ::Atom suggested_action = event.data.l[4];
494 if (!target_current_context_.get()) {
495 NOTREACHED();
496 return;
499 // If we already have all the data from this drag, we complete it
500 // immediately.
501 target_current_context_->OnStartXdndPositionMessage(
502 this, suggested_action, source_window,
503 gfx::Point(x_root_window, y_root_window));
506 void DesktopDragDropClientAuraX11::OnXdndStatus(
507 const XClientMessageEvent& event) {
508 DVLOG(1) << "XdndStatus";
510 unsigned long source_window = event.data.l[0];
512 if (source_window != source_current_window_)
513 return;
515 if (source_state_ != SOURCE_STATE_PENDING_DROP &&
516 source_state_ != SOURCE_STATE_OTHER) {
517 return;
520 waiting_on_status_ = false;
521 status_received_since_enter_ = true;
523 if (event.data.l[1] & 1) {
524 ::Atom atom_operation = event.data.l[4];
525 negotiated_operation_ = AtomToDragOperation(atom_operation);
526 } else {
527 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
530 if (source_state_ == SOURCE_STATE_PENDING_DROP) {
531 // We were waiting on the status message so we could send the XdndDrop.
532 if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
533 move_loop_->EndMoveLoop();
534 return;
536 source_state_ = SOURCE_STATE_DROPPED;
537 SendXdndDrop(source_window);
538 return;
541 int cursor_type = ui::kCursorNull;
542 switch (negotiated_operation_) {
543 case ui::DragDropTypes::DRAG_COPY:
544 cursor_type = ui::kCursorCopy;
545 break;
546 case ui::DragDropTypes::DRAG_MOVE:
547 cursor_type = ui::kCursorMove;
548 break;
549 default:
550 cursor_type = ui::kCursorGrabbing;
551 break;
553 move_loop_->UpdateCursor(cursor_manager_->GetInitializedCursor(cursor_type));
555 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
556 // window to not send further XdndPosition messages while the cursor is
557 // within it. However, it is considered advisory and (at least according to
558 // the spec) the other side must handle further position messages within
559 // it. GTK+ doesn't bother with this, so neither should we.
561 if (next_position_message_.get()) {
562 // We were waiting on the status message so we could send off the next
563 // position message we queued up.
564 gfx::Point p = next_position_message_->first;
565 unsigned long event_time = next_position_message_->second;
566 next_position_message_.reset();
568 SendXdndPosition(source_window, p, event_time);
572 void DesktopDragDropClientAuraX11::OnXdndFinished(
573 const XClientMessageEvent& event) {
574 DVLOG(1) << "XdndFinished";
575 unsigned long source_window = event.data.l[0];
576 if (source_current_window_ != source_window)
577 return;
579 // Clear |negotiated_operation_| if the drag was rejected.
580 if ((event.data.l[1] & 1) == 0)
581 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
583 // Clear |source_current_window_| to avoid sending XdndLeave upon ending the
584 // move loop.
585 source_current_window_ = None;
586 move_loop_->EndMoveLoop();
589 void DesktopDragDropClientAuraX11::OnXdndDrop(
590 const XClientMessageEvent& event) {
591 DVLOG(1) << "XdndDrop";
593 unsigned long source_window = event.data.l[0];
595 int drag_operation = ui::DragDropTypes::DRAG_NONE;
596 if (target_window_) {
597 aura::client::DragDropDelegate* delegate =
598 aura::client::GetDragDropDelegate(target_window_);
599 if (delegate) {
600 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
601 xwindow_, target_current_context_->fetched_targets()));
603 ui::DropTargetEvent event(data,
604 target_window_location_,
605 target_window_root_location_,
606 target_current_context_->GetDragOperation());
607 if (target_current_context_->source_client()) {
608 event.set_flags(target_current_context_->source_client()
609 ->current_modifier_state());
610 } else {
611 event.set_flags(XGetModifiers());
614 if (!IsDragDropInProgress()) {
615 UMA_HISTOGRAM_COUNTS("Event.DragDrop.ExternalOriginDrop", 1);
618 drag_operation = delegate->OnPerformDrop(event);
621 target_window_->RemoveObserver(this);
622 target_window_ = NULL;
625 XEvent xev;
626 xev.xclient.type = ClientMessage;
627 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
628 xev.xclient.format = 32;
629 xev.xclient.window = source_window;
630 xev.xclient.data.l[0] = xwindow_;
631 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
632 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
634 SendXClientEvent(source_window, &xev);
637 void DesktopDragDropClientAuraX11::OnSelectionNotify(
638 const XSelectionEvent& xselection) {
639 if (target_current_context_)
640 target_current_context_->OnSelectionNotify(xselection);
642 // ICCCM requires us to delete the property passed into SelectionNotify.
643 if (xselection.property != None)
644 XDeleteProperty(xdisplay_, xwindow_, xselection.property);
647 int DesktopDragDropClientAuraX11::StartDragAndDrop(
648 const ui::OSExchangeData& data,
649 aura::Window* root_window,
650 aura::Window* source_window,
651 const gfx::Point& root_location,
652 int operation,
653 ui::DragDropTypes::DragEventSource source) {
654 UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Start", source,
655 ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT);
657 source_current_window_ = None;
658 DCHECK(!g_current_drag_drop_client);
659 g_current_drag_drop_client = this;
660 waiting_on_status_ = false;
661 next_position_message_.reset();
662 status_received_since_enter_ = false;
663 source_state_ = SOURCE_STATE_OTHER;
664 drag_operation_ = operation;
665 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
667 const ui::OSExchangeData::Provider* provider = &data.provider();
668 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
669 provider);
671 source_provider_->TakeOwnershipOfSelection();
673 std::vector< ::Atom> actions = GetOfferedDragOperations();
674 if (!source_provider_->file_contents_name().empty()) {
675 actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
676 ui::SetStringProperty(
677 xwindow_,
678 atom_cache_.GetAtom(kXdndDirectSave0),
679 atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
680 source_provider_->file_contents_name().AsUTF8Unsafe());
682 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
684 gfx::ImageSkia drag_image = source_provider_->GetDragImage();
685 if (IsValidDragImage(drag_image)) {
686 CreateDragWidget(drag_image);
687 drag_widget_offset_ = source_provider_->GetDragImageOffset();
690 // Chrome expects starting drag and drop to release capture.
691 aura::Window* capture_window =
692 aura::client::GetCaptureClient(root_window)->GetGlobalCaptureWindow();
693 if (capture_window)
694 capture_window->ReleaseCapture();
696 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
697 // move loop, which would also destroy this drag-client. So keep track of
698 // whether it is alive after the drag ends.
699 base::WeakPtr<DesktopDragDropClientAuraX11> alive(
700 weak_ptr_factory_.GetWeakPtr());
702 // Windows has a specific method, DoDragDrop(), which performs the entire
703 // drag. We have to emulate this, so we spin off a nested runloop which will
704 // track all cursor movement and reroute events to a specific handler.
705 move_loop_->RunMoveLoop(
706 source_window,
707 cursor_manager_->GetInitializedCursor(ui::kCursorGrabbing));
709 if (alive) {
710 if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
711 UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Cancel", source,
712 ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT);
713 } else {
714 UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Drop", source,
715 ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT);
717 drag_widget_.reset();
719 source_provider_ = NULL;
720 g_current_drag_drop_client = NULL;
721 drag_operation_ = 0;
722 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
723 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
725 return negotiated_operation_;
727 UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Cancel", source,
728 ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT);
729 return ui::DragDropTypes::DRAG_NONE;
732 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
733 const ui::LocatedEvent& event) {
734 NOTIMPLEMENTED();
737 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
738 const ui::LocatedEvent& event) {
739 NOTIMPLEMENTED();
742 void DesktopDragDropClientAuraX11::DragCancel() {
743 move_loop_->EndMoveLoop();
746 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
747 return !!g_current_drag_drop_client;
750 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
751 DCHECK_EQ(target_window_, window);
752 target_window_ = NULL;
755 void DesktopDragDropClientAuraX11::OnMouseMovement(
756 const gfx::Point& screen_point,
757 int flags,
758 base::TimeDelta event_time) {
759 if (drag_widget_.get()) {
760 drag_widget_->SetBounds(
761 gfx::Rect(screen_point - drag_widget_offset_,
762 drag_widget_->GetWindowBoundsInScreen().size()));
763 drag_widget_->StackAtTop();
766 const int kModifiers = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
767 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN;
768 current_modifier_state_ = flags & kModifiers;
770 repeat_mouse_move_timer_.Stop();
771 ProcessMouseMove(screen_point, event_time.InMilliseconds());
774 void DesktopDragDropClientAuraX11::OnMouseReleased() {
775 repeat_mouse_move_timer_.Stop();
777 if (source_state_ != SOURCE_STATE_OTHER) {
778 // The user has previously released the mouse and is clicking in
779 // frustration.
780 move_loop_->EndMoveLoop();
781 return;
784 if (source_current_window_ != None) {
785 if (waiting_on_status_) {
786 if (status_received_since_enter_) {
787 // If we are waiting for an XdndStatus message, we need to wait for it
788 // to complete.
789 source_state_ = SOURCE_STATE_PENDING_DROP;
791 // Start timer to end the move loop if the target takes too long to send
792 // the XdndStatus and XdndFinished messages.
793 StartEndMoveLoopTimer();
794 return;
797 move_loop_->EndMoveLoop();
798 return;
801 if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) {
802 // Start timer to end the move loop if the target takes too long to send
803 // an XdndFinished message. It is important that StartEndMoveLoopTimer()
804 // is called before SendXdndDrop() because SendXdndDrop()
805 // sends XdndFinished synchronously if the drop target is a Chrome
806 // window.
807 StartEndMoveLoopTimer();
809 // We have negotiated an action with the other end.
810 source_state_ = SOURCE_STATE_DROPPED;
811 SendXdndDrop(source_current_window_);
812 return;
816 move_loop_->EndMoveLoop();
819 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
820 if (source_current_window_ != None) {
821 SendXdndLeave(source_current_window_);
822 source_current_window_ = None;
824 target_current_context_.reset();
825 repeat_mouse_move_timer_.Stop();
826 end_move_loop_timer_.Stop();
829 scoped_ptr<X11MoveLoop> DesktopDragDropClientAuraX11::CreateMoveLoop(
830 X11MoveLoopDelegate* delegate) {
831 return make_scoped_ptr(new X11WholeScreenMoveLoop(this));
834 XID DesktopDragDropClientAuraX11::FindWindowFor(
835 const gfx::Point& screen_point) {
836 views::X11TopmostWindowFinder finder;
837 ::Window target = finder.FindWindowAt(screen_point);
839 if (target == None)
840 return None;
842 // Figure out which window we should test as XdndAware. If |target| has
843 // XdndProxy, it will set that proxy on target, and if not, |target|'s
844 // original value will remain.
845 ui::GetXIDProperty(target, "XdndProxy", &target);
847 int version;
848 if (ui::GetIntProperty(target, "XdndAware", &version) &&
849 version >= kMinXdndVersion) {
850 return target;
852 return None;
855 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
856 XEvent* xev) {
857 DCHECK_EQ(ClientMessage, xev->type);
859 // Don't send messages to the X11 message queue if we can help it.
860 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
861 if (short_circuit) {
862 Atom message_type = xev->xclient.message_type;
863 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
864 short_circuit->OnXdndEnter(xev->xclient);
865 return;
866 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
867 short_circuit->OnXdndLeave(xev->xclient);
868 return;
869 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
870 short_circuit->OnXdndPosition(xev->xclient);
871 return;
872 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
873 short_circuit->OnXdndStatus(xev->xclient);
874 return;
875 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
876 short_circuit->OnXdndFinished(xev->xclient);
877 return;
878 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
879 short_circuit->OnXdndDrop(xev->xclient);
880 return;
884 // I don't understand why the GTK+ code is doing what it's doing here. It
885 // goes out of its way to send the XEvent so that it receives a callback on
886 // success or failure, and when it fails, it then sends an internal
887 // GdkEvent about the failed drag. (And sending this message doesn't appear
888 // to go through normal xlib machinery, but instead passes through the low
889 // level xProto (the x11 wire format) that I don't understand.
891 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
892 // sufficient.
893 XSendEvent(xdisplay_, xid, False, 0, xev);
896 void DesktopDragDropClientAuraX11::ProcessMouseMove(
897 const gfx::Point& screen_point,
898 unsigned long event_time) {
899 if (source_state_ != SOURCE_STATE_OTHER)
900 return;
902 // Find the current window the cursor is over.
903 ::Window dest_window = FindWindowFor(screen_point);
905 if (source_current_window_ != dest_window) {
906 if (source_current_window_ != None)
907 SendXdndLeave(source_current_window_);
909 source_current_window_ = dest_window;
910 waiting_on_status_ = false;
911 next_position_message_.reset();
912 status_received_since_enter_ = false;
913 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
915 if (source_current_window_ != None)
916 SendXdndEnter(source_current_window_);
919 if (source_current_window_ != None) {
920 if (waiting_on_status_) {
921 next_position_message_.reset(
922 new std::pair<gfx::Point, unsigned long>(screen_point, event_time));
923 } else {
924 SendXdndPosition(dest_window, screen_point, event_time);
929 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
930 end_move_loop_timer_.Start(FROM_HERE,
931 base::TimeDelta::FromMilliseconds(
932 kEndMoveLoopTimeoutMs),
933 this,
934 &DesktopDragDropClientAuraX11::EndMoveLoop);
937 void DesktopDragDropClientAuraX11::EndMoveLoop() {
938 move_loop_->EndMoveLoop();
941 void DesktopDragDropClientAuraX11::DragTranslate(
942 const gfx::Point& root_window_location,
943 scoped_ptr<ui::OSExchangeData>* data,
944 scoped_ptr<ui::DropTargetEvent>* event,
945 aura::client::DragDropDelegate** delegate) {
946 gfx::Point root_location = root_window_location;
947 root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
948 aura::Window* target_window =
949 root_window_->GetEventHandlerForPoint(root_location);
950 bool target_window_changed = false;
951 if (target_window != target_window_) {
952 if (target_window_)
953 NotifyDragLeave();
954 target_window_ = target_window;
955 if (target_window_)
956 target_window_->AddObserver(this);
957 target_window_changed = true;
959 *delegate = NULL;
960 if (!target_window_)
961 return;
962 *delegate = aura::client::GetDragDropDelegate(target_window_);
963 if (!*delegate)
964 return;
966 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
967 xwindow_, target_current_context_->fetched_targets())));
968 gfx::Point location = root_location;
969 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
971 target_window_location_ = location;
972 target_window_root_location_ = root_location;
974 int drag_op = target_current_context_->GetDragOperation();
975 // KDE-based file browsers such as Dolphin change the drag operation depending
976 // on whether alt/ctrl/shift was pressed. However once Chromium gets control
977 // over the X11 events, the source application does no longer receive X11
978 // events for key modifier changes, so the dnd operation gets stuck in an
979 // incorrect state. Blink can only dnd-open files of type DRAG_COPY, so the
980 // DRAG_COPY mask is added if the dnd object is a file.
981 if (drag_op & (ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_LINK) &&
982 data->get()->HasFile()) {
983 drag_op |= ui::DragDropTypes::DRAG_COPY;
986 event->reset(new ui::DropTargetEvent(
987 *(data->get()),
988 location,
989 root_location,
990 drag_op));
991 if (target_current_context_->source_client()) {
992 (*event)->set_flags(
993 target_current_context_->source_client()->current_modifier_state());
994 } else {
995 (*event)->set_flags(XGetModifiers());
997 if (target_window_changed)
998 (*delegate)->OnDragEntered(*event->get());
1001 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
1002 if (!target_window_)
1003 return;
1004 DragDropDelegate* delegate =
1005 aura::client::GetDragDropDelegate(target_window_);
1006 if (delegate)
1007 delegate->OnDragExited();
1008 target_window_->RemoveObserver(this);
1009 target_window_ = NULL;
1012 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
1013 int drag_operation) {
1014 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
1015 return atom_cache_.GetAtom(kXdndActionCopy);
1016 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
1017 return atom_cache_.GetAtom(kXdndActionMove);
1018 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
1019 return atom_cache_.GetAtom(kXdndActionLink);
1021 return None;
1024 ui::DragDropTypes::DragOperation
1025 DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
1026 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
1027 return ui::DragDropTypes::DRAG_COPY;
1028 if (atom == atom_cache_.GetAtom(kXdndActionMove))
1029 return ui::DragDropTypes::DRAG_MOVE;
1030 if (atom == atom_cache_.GetAtom(kXdndActionLink))
1031 return ui::DragDropTypes::DRAG_LINK;
1033 return ui::DragDropTypes::DRAG_NONE;
1036 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
1037 std::vector< ::Atom> operations;
1038 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
1039 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
1040 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
1041 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
1042 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
1043 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
1044 return operations;
1047 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
1048 return source_provider_ ? source_provider_->GetFormatMap() :
1049 ui::SelectionFormatMap();
1052 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
1053 ::Window source_window,
1054 const gfx::Point& screen_point) {
1055 int drag_operation = ui::DragDropTypes::DRAG_NONE;
1056 scoped_ptr<ui::OSExchangeData> data;
1057 scoped_ptr<ui::DropTargetEvent> drop_target_event;
1058 DragDropDelegate* delegate = NULL;
1059 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
1060 if (delegate)
1061 drag_operation = delegate->OnDragUpdated(*drop_target_event);
1063 // Sends an XdndStatus message back to the source_window. l[2,3]
1064 // theoretically represent an area in the window where the current action is
1065 // the same as what we're returning, but I can't find any implementation that
1066 // actually making use of this. A client can return (0, 0) and/or set the
1067 // first bit of l[1] to disable the feature, and it appears that gtk neither
1068 // sets this nor respects it if set.
1069 XEvent xev;
1070 xev.xclient.type = ClientMessage;
1071 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
1072 xev.xclient.format = 32;
1073 xev.xclient.window = source_window;
1074 xev.xclient.data.l[0] = xwindow_;
1075 xev.xclient.data.l[1] = (drag_operation != 0) ?
1076 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
1077 xev.xclient.data.l[2] = 0;
1078 xev.xclient.data.l[3] = 0;
1079 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
1081 SendXClientEvent(source_window, &xev);
1084 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
1085 XEvent xev;
1086 xev.xclient.type = ClientMessage;
1087 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
1088 xev.xclient.format = 32;
1089 xev.xclient.window = dest_window;
1090 xev.xclient.data.l[0] = xwindow_;
1091 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
1092 xev.xclient.data.l[2] = 0;
1093 xev.xclient.data.l[3] = 0;
1094 xev.xclient.data.l[4] = 0;
1096 std::vector<Atom> targets;
1097 source_provider_->RetrieveTargets(&targets);
1099 if (targets.size() > 3) {
1100 xev.xclient.data.l[1] |= 1;
1101 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
1102 } else {
1103 // Pack the targets into the enter message.
1104 for (size_t i = 0; i < targets.size(); ++i)
1105 xev.xclient.data.l[2 + i] = targets[i];
1108 SendXClientEvent(dest_window, &xev);
1111 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
1112 XEvent xev;
1113 xev.xclient.type = ClientMessage;
1114 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
1115 xev.xclient.format = 32;
1116 xev.xclient.window = dest_window;
1117 xev.xclient.data.l[0] = xwindow_;
1118 xev.xclient.data.l[1] = 0;
1119 xev.xclient.data.l[2] = 0;
1120 xev.xclient.data.l[3] = 0;
1121 xev.xclient.data.l[4] = 0;
1122 SendXClientEvent(dest_window, &xev);
1125 void DesktopDragDropClientAuraX11::SendXdndPosition(
1126 ::Window dest_window,
1127 const gfx::Point& screen_point,
1128 unsigned long event_time) {
1129 waiting_on_status_ = true;
1131 XEvent xev;
1132 xev.xclient.type = ClientMessage;
1133 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
1134 xev.xclient.format = 32;
1135 xev.xclient.window = dest_window;
1136 xev.xclient.data.l[0] = xwindow_;
1137 xev.xclient.data.l[1] = 0;
1138 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
1139 xev.xclient.data.l[3] = event_time;
1140 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
1141 SendXClientEvent(dest_window, &xev);
1143 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
1144 // the Xdnd protocol both recommend that drag events should be sent
1145 // periodically.
1146 repeat_mouse_move_timer_.Start(
1147 FROM_HERE,
1148 base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
1149 base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
1150 base::Unretained(this),
1151 screen_point,
1152 event_time));
1155 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
1156 XEvent xev;
1157 xev.xclient.type = ClientMessage;
1158 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
1159 xev.xclient.format = 32;
1160 xev.xclient.window = dest_window;
1161 xev.xclient.data.l[0] = xwindow_;
1162 xev.xclient.data.l[1] = 0;
1163 xev.xclient.data.l[2] = CurrentTime;
1164 xev.xclient.data.l[3] = None;
1165 xev.xclient.data.l[4] = None;
1166 SendXClientEvent(dest_window, &xev);
1169 void DesktopDragDropClientAuraX11::CreateDragWidget(
1170 const gfx::ImageSkia& image) {
1171 Widget* widget = new Widget;
1172 Widget::InitParams params(Widget::InitParams::TYPE_DRAG);
1173 params.opacity = Widget::InitParams::OPAQUE_WINDOW;
1174 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1175 params.accept_events = false;
1177 gfx::Point location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint() -
1178 drag_widget_offset_;
1179 params.bounds = gfx::Rect(location, image.size());
1180 widget->set_focus_on_creation(false);
1181 widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE);
1182 widget->Init(params);
1183 widget->SetOpacity(kDragWidgetOpacity);
1184 widget->GetNativeWindow()->SetName("DragWindow");
1186 ImageView* image_view = new ImageView();
1187 image_view->SetImage(image);
1188 image_view->SetBounds(0, 0, image.width(), image.height());
1189 widget->SetContentsView(image_view);
1190 widget->Show();
1191 widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false);
1193 drag_widget_.reset(widget);
1196 bool DesktopDragDropClientAuraX11::IsValidDragImage(
1197 const gfx::ImageSkia& image) {
1198 if (image.isNull())
1199 return false;
1201 // Because we need a GL context per window, we do a quick check so that we
1202 // don't make another context if the window would just be displaying a mostly
1203 // transparent image.
1204 const SkBitmap* in_bitmap = image.bitmap();
1205 SkAutoLockPixels in_lock(*in_bitmap);
1206 for (int y = 0; y < in_bitmap->height(); ++y) {
1207 uint32* in_row = in_bitmap->getAddr32(0, y);
1209 for (int x = 0; x < in_bitmap->width(); ++x) {
1210 if (SkColorGetA(in_row[x]) > kMinAlpha)
1211 return true;
1215 return false;
1218 } // namespace views