Home | History | Annotate | Download | only in desktop_aura
      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   virtual ~X11DragContext();
    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   virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
    148   virtual 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   event->reset(new ui::DropTargetEvent(
    912       *(data->get()),
    913       location,
    914       root_location,
    915       target_current_context_->GetDragOperation()));
    916   if (target_window_changed)
    917     (*delegate)->OnDragEntered(*event->get());
    918 }
    919 
    920 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
    921   if (!target_window_)
    922     return;
    923   DragDropDelegate* delegate =
    924       aura::client::GetDragDropDelegate(target_window_);
    925   if (delegate)
    926     delegate->OnDragExited();
    927   target_window_->RemoveObserver(this);
    928   target_window_ = NULL;
    929 }
    930 
    931 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
    932     int drag_operation) {
    933   if (drag_operation & ui::DragDropTypes::DRAG_COPY)
    934     return atom_cache_.GetAtom(kXdndActionCopy);
    935   if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
    936     return atom_cache_.GetAtom(kXdndActionMove);
    937   if (drag_operation & ui::DragDropTypes::DRAG_LINK)
    938     return atom_cache_.GetAtom(kXdndActionLink);
    939 
    940   return None;
    941 }
    942 
    943 ui::DragDropTypes::DragOperation
    944 DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
    945   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
    946     return ui::DragDropTypes::DRAG_COPY;
    947   if (atom == atom_cache_.GetAtom(kXdndActionMove))
    948     return ui::DragDropTypes::DRAG_MOVE;
    949   if (atom == atom_cache_.GetAtom(kXdndActionLink))
    950     return ui::DragDropTypes::DRAG_LINK;
    951 
    952   return ui::DragDropTypes::DRAG_NONE;
    953 }
    954 
    955 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
    956   std::vector< ::Atom> operations;
    957   if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
    958     operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
    959   if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
    960     operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
    961   if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
    962     operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
    963   return operations;
    964 }
    965 
    966 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
    967   return source_provider_ ? source_provider_->GetFormatMap() :
    968       ui::SelectionFormatMap();
    969 }
    970 
    971 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
    972     ::Window source_window,
    973     const gfx::Point& screen_point) {
    974   int drag_operation = ui::DragDropTypes::DRAG_NONE;
    975   scoped_ptr<ui::OSExchangeData> data;
    976   scoped_ptr<ui::DropTargetEvent> drop_target_event;
    977   DragDropDelegate* delegate = NULL;
    978   DragTranslate(screen_point, &data, &drop_target_event, &delegate);
    979   if (delegate)
    980     drag_operation = delegate->OnDragUpdated(*drop_target_event);
    981 
    982   // Sends an XdndStatus message back to the source_window. l[2,3]
    983   // theoretically represent an area in the window where the current action is
    984   // the same as what we're returning, but I can't find any implementation that
    985   // actually making use of this. A client can return (0, 0) and/or set the
    986   // first bit of l[1] to disable the feature, and it appears that gtk neither
    987   // sets this nor respects it if set.
    988   XEvent xev;
    989   xev.xclient.type = ClientMessage;
    990   xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
    991   xev.xclient.format = 32;
    992   xev.xclient.window = source_window;
    993   xev.xclient.data.l[0] = xwindow_;
    994   xev.xclient.data.l[1] = (drag_operation != 0) ?
    995       (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
    996   xev.xclient.data.l[2] = 0;
    997   xev.xclient.data.l[3] = 0;
    998   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
    999 
   1000   SendXClientEvent(source_window, &xev);
   1001 }
   1002 
   1003 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
   1004   XEvent xev;
   1005   xev.xclient.type = ClientMessage;
   1006   xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
   1007   xev.xclient.format = 32;
   1008   xev.xclient.window = dest_window;
   1009   xev.xclient.data.l[0] = xwindow_;
   1010   xev.xclient.data.l[1] = (kMinXdndVersion << 24);  // The version number.
   1011   xev.xclient.data.l[2] = 0;
   1012   xev.xclient.data.l[3] = 0;
   1013   xev.xclient.data.l[4] = 0;
   1014 
   1015   std::vector<Atom> targets;
   1016   source_provider_->RetrieveTargets(&targets);
   1017 
   1018   if (targets.size() > 3) {
   1019     xev.xclient.data.l[1] |= 1;
   1020     ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
   1021   } else {
   1022     // Pack the targets into the enter message.
   1023     for (size_t i = 0; i < targets.size(); ++i)
   1024       xev.xclient.data.l[2 + i] = targets[i];
   1025   }
   1026 
   1027   SendXClientEvent(dest_window, &xev);
   1028 }
   1029 
   1030 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
   1031   XEvent xev;
   1032   xev.xclient.type = ClientMessage;
   1033   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
   1034   xev.xclient.format = 32;
   1035   xev.xclient.window = dest_window;
   1036   xev.xclient.data.l[0] = xwindow_;
   1037   xev.xclient.data.l[1] = 0;
   1038   xev.xclient.data.l[2] = 0;
   1039   xev.xclient.data.l[3] = 0;
   1040   xev.xclient.data.l[4] = 0;
   1041   SendXClientEvent(dest_window, &xev);
   1042 }
   1043 
   1044 void DesktopDragDropClientAuraX11::SendXdndPosition(
   1045     ::Window dest_window,
   1046     const gfx::Point& screen_point,
   1047     unsigned long event_time) {
   1048   waiting_on_status_ = true;
   1049 
   1050   XEvent xev;
   1051   xev.xclient.type = ClientMessage;
   1052   xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
   1053   xev.xclient.format = 32;
   1054   xev.xclient.window = dest_window;
   1055   xev.xclient.data.l[0] = xwindow_;
   1056   xev.xclient.data.l[1] = 0;
   1057   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
   1058   xev.xclient.data.l[3] = event_time;
   1059   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
   1060   SendXClientEvent(dest_window, &xev);
   1061 
   1062   // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
   1063   // the Xdnd protocol both recommend that drag events should be sent
   1064   // periodically.
   1065   repeat_mouse_move_timer_.Start(
   1066       FROM_HERE,
   1067       base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
   1068       base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
   1069                  base::Unretained(this),
   1070                  screen_point,
   1071                  event_time));
   1072 }
   1073 
   1074 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
   1075   XEvent xev;
   1076   xev.xclient.type = ClientMessage;
   1077   xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
   1078   xev.xclient.format = 32;
   1079   xev.xclient.window = dest_window;
   1080   xev.xclient.data.l[0] = xwindow_;
   1081   xev.xclient.data.l[1] = 0;
   1082   xev.xclient.data.l[2] = CurrentTime;
   1083   xev.xclient.data.l[3] = None;
   1084   xev.xclient.data.l[4] = None;
   1085   SendXClientEvent(dest_window, &xev);
   1086 }
   1087 
   1088 void DesktopDragDropClientAuraX11::CreateDragWidget(
   1089     const gfx::ImageSkia& image) {
   1090   Widget* widget = new Widget;
   1091   Widget::InitParams params(Widget::InitParams::TYPE_DRAG);
   1092   params.opacity = Widget::InitParams::OPAQUE_WINDOW;
   1093   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   1094   params.accept_events = false;
   1095 
   1096   gfx::Point location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint() -
   1097                         drag_widget_offset_;
   1098   params.bounds = gfx::Rect(location, image.size());
   1099   widget->set_focus_on_creation(false);
   1100   widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE);
   1101   widget->Init(params);
   1102   widget->SetOpacity(kDragWidgetOpacity);
   1103   widget->GetNativeWindow()->SetName("DragWindow");
   1104 
   1105   ImageView* image_view = new ImageView();
   1106   image_view->SetImage(image);
   1107   image_view->SetBounds(0, 0, image.width(), image.height());
   1108   widget->SetContentsView(image_view);
   1109   widget->Show();
   1110   widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false);
   1111 
   1112   drag_widget_.reset(widget);
   1113 }
   1114 
   1115 bool DesktopDragDropClientAuraX11::IsValidDragImage(
   1116     const gfx::ImageSkia& image) {
   1117   if (image.isNull())
   1118     return false;
   1119 
   1120   // Because we need a GL context per window, we do a quick check so that we
   1121   // don't make another context if the window would just be displaying a mostly
   1122   // transparent image.
   1123   const SkBitmap* in_bitmap = image.bitmap();
   1124   SkAutoLockPixels in_lock(*in_bitmap);
   1125   for (int y = 0; y < in_bitmap->height(); ++y) {
   1126     uint32* in_row = in_bitmap->getAddr32(0, y);
   1127 
   1128     for (int x = 0; x < in_bitmap->width(); ++x) {
   1129       if (SkColorGetA(in_row[x]) > kMinAlpha)
   1130         return true;
   1131     }
   1132   }
   1133 
   1134   return false;
   1135 }
   1136 
   1137 }  // namespace views
   1138